diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 844cd7a67..000000000
--- a/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-*.hex
-*.o
-*.elf
-*.DS_Store
-*.d
diff --git a/Makefile b/Makefile
index 05f53d9dd..9e20a438a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,7 @@
# Part of Grbl
#
# Copyright (c) 2009-2011 Simen Svale Skogsrud
-# Copyright (c) 2012 Sungeun K. Jeon
-# Copyright (c) 2014 Bob Beattie
+# 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
@@ -29,36 +28,45 @@
# is connected.
# FUSES ........ Parameters for avrdude to flash the fuses appropriately.
-DEVICE ?= atmega2560
+DEVICE ?= atmega328p
CLOCK = 16000000
-PROGRAMMER ?= -c wiring -P /dev/tty.usbmodem1411
-OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \
- protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \
- print.o probe.o report.o system.o
+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 jog.c\
+ print.c probe.c report.c system.c
+BUILDDIR = build
+SOURCEDIR = grbl
# FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
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 115200 -D
-COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections
+AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F
+
+# Compile flags for avr-gcc v4.8.1. Does not produce -flto warnings.
+# COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections
+
+# Compile flags for avr-gcc v4.9.2 compatible with the IDE. Or if you don't care about the warnings.
+COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections -flto
+
+
+OBJECTS = $(addprefix $(BUILDDIR)/,$(notdir $(SOURCE:.c=.o)))
# symbolic targets:
all: grbl.hex
-.c.o:
- $(COMPILE) -c $< -o $@
- @$(COMPILE) -MM $< > $*.d
+$(BUILDDIR)/%.o: $(SOURCEDIR)/%.c
+ $(COMPILE) -MMD -MP -c $< -o $@
.S.o:
- $(COMPILE) -x assembler-with-cpp -c $< -o $@
+ $(COMPILE) -x assembler-with-cpp -c $< -o $(BUILDDIR)/$@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.
-.c.s:
- $(COMPILE) -S $< -o $@
+#.c.s:
+ $(COMPILE) -S $< -o $(BUILDDIR)/$@
flash: all
$(AVRDUDE) -U flash:w:grbl.hex:i
@@ -74,25 +82,25 @@ load: all
bootloadHID grbl.hex
clean:
- rm -f grbl.hex main.elf $(OBJECTS) $(OBJECTS:.o=.d)
+ rm -f grbl.hex $(BUILDDIR)/*.o $(BUILDDIR)/*.d $(BUILDDIR)/*.elf
# file targets:
-main.elf: $(OBJECTS)
- $(COMPILE) -o main.elf $(OBJECTS) -lm -Wl,--gc-sections
+$(BUILDDIR)/main.elf: $(OBJECTS)
+ $(COMPILE) -o $(BUILDDIR)/main.elf $(OBJECTS) -lm -Wl,--gc-sections
-grbl.hex: main.elf
+grbl.hex: $(BUILDDIR)/main.elf
rm -f grbl.hex
- avr-objcopy -j .text -j .data -O ihex main.elf grbl.hex
- avr-size --format=berkeley main.elf
+ avr-objcopy -j .text -j .data -O ihex $(BUILDDIR)/main.elf grbl.hex
+ avr-size --format=berkeley $(BUILDDIR)/main.elf
# If you have an EEPROM section, you must also create a hex file for the
# EEPROM and add it to the "flash" target.
# Targets for code debugging and analysis:
disasm: main.elf
- avr-objdump -d main.elf
+ avr-objdump -d $(BUILDDIR)/main.elf
cpp:
- $(COMPILE) -E main.c
+ $(COMPILE) -E $(SOURCEDIR)/main.c
# include generated header dependencies
--include $(OBJECTS:.o=.d)
+-include $(BUILDDIR)/$(OBJECTS:.o=.d)
diff --git a/README.md b/README.md
index 6c6bb17b5..442e3654e 100644
--- a/README.md
+++ b/README.md
@@ -1,107 +1,53 @@
-# OpenPnP-Grbl - An embedded g-code interpreter and motion-controller for the Arduino/AVR328/Mega2560 microcontroller used with the OpenPnP project.
-
-***
-
-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/Mega) as long as it sports an Atmega 328/2560.
-
-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.
-
-* [Licensing](https://github.com/grbl/grbl/wiki/Licensing): Grbl v0.9 is free software, released under the GPLv3 license. Obsolete versions of Grbl, v0.8 and prior, are released under the permissive MIT-license. This will ensure Grbl will always be an open-source project while making the code permissive for others.
-
-* For more information and help, check out our future **[Wiki pages!](https://github.com/maaadbob/openpnp-grbl/wiki)** If you find that the information is out-dated, please help us keep it updated by notifying me! Thanks!
-
-* Happy-go-lucky Developer [_2014_]: Bob Beattie, (United Kingdom)
-
-* Lead Developer [_2011 - Current_]: Sonny Jeon, Ph.D. (USA)
-
-* Lead Developer [_2009 - 2011_]: Simen Svale Skogsrud (Norway). aka The Originator/Creator/Pioneer/Father of Grbl.
-
-***
-
-_**Master Branch:**_
-* [Grbl v0.97 AtMega2560 16MHz 9600 baud with generic defaults](https://github.com/maaadbob/openpnp-grbl) _(2014-09-07)_
-* [Grbl v0.9g Atmega328p 16mhz 115200baud with generic defaults](http://bit.ly/1m8E1Qa) _(2014-08-22)_
-* [Grbl v0.9g Atmega328p 16mhz 115200baud with ShapeOko2 defaults](http://bit.ly/1kOAzig) _(2014-08-22)_
- - **IMPORTANT INFO WHEN UPGRADING TO GRBL v0.9g:**
- - Baudrate is now **115200** (Up from 9600).
- - Settings WILL be overwritten. Please make sure you have a backup. Also, settings have been renumbered and some have changed how they work. See our [Configuring v0.9 Wiki page](https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.9) for details.
-
-_**Archives:**_
-* [Grbl v0.8c Atmega328p 16mhz 9600baud](http://bit.ly/SSdCJE)
-* [Grbl v0.7d Atmega328p 16mhz 9600baud](http://bit.ly/ZhL15G)
-* [Grbl v0.6b Atmega328p 16mhz 9600baud](http://bit.ly/VD04A5)
-* [Grbl v0.51 Atmega328p 16mhz 9600baud](http://bit.ly/W75BS1)
-* [Grbl v0.6b Atmega168 16mhz 9600baud](http://bit.ly/SScWnE)
-* [Grbl v0.51 Atmega168 16mhz 9600baud](http://bit.ly/VXyrYu)
-
-## Building from Source
-
-You need avr-gcc and make in your PATH to build, and avrdude to flash. Both come with the Arduino IDE, so the easiest method is to install that and set your path
-to include the path to the included avr tools. On Mac that is:
-
-```
-export PATH=$PATH:/Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin
-```
-
-Then edit the Makefile and change the PROGRAMMER line to include the right serial port for your Arduino:
-```
-PROGRAMMER ?= -c wiring -P /dev/tty.usbmodem1411
-```
-
-Finally, you can build and flash with:
-```
-make clean
-make
-make flash
-```
-
-
-***
-## Update Summary for OpenPnP
- - Additional axis 'C' added.
-
-## Update Summary for v0.9 from v0.8
- - **IMPORTANT: Default serial baudrate is now 115200! (Up from 9600). And your settings will be over-written! Make sure to have a backup.**
- - **_NEW_ Super Smooth Stepper Algorithm:** Complete overhaul of the handling of the stepper driver to simplify and reduce task time per ISR tick. Much smoother operation with the new Adaptive Multi-Axis Step Smoothing (AMASS) algorithm which does what its name implies (see stepper.c source for details). Users should immediately see significant improvements in how their machines move and overall performance!
- - **Stability and Robustness Updates:** Grbl's overall stability has been focused on for this version. The planner and step-execution interface has been completely re-written for robustness and incorruptibility by the introduction of an intermediate step segment buffer that "checks-out" steps from the planner buffer in real-time. This means we can now fearlessly drive Grbl to it's highest limits. Combined with the new stepper algorithm and planner optimizations, this translated to **5x to 10x** overall performance increases in our testing! Also, stability and robustness tests have been reported to easily take 1.4 million (yes, **million**) line g-code programs like a champ!
- - **(x4)+ Faster Planner:** Planning computations improved four-fold or more by optimizing end-to-end operations, which included streamlining the computations and introducing a planner pointer to locate un-improvable portions of the buffer and not waste cycles recomputing them.
- - **Compile-able via Arduino IDE!:** Grbl's source code may be now download and altered, and then be compiled and flashed directly through the Arduino IDE, which should work on all platforms. See the Wiki for details on how to do it.
- - **G-Code Parser Overhaul:** Completely re-written from the ground-up for 100%-compliance* to the g-code standard. (* Parts of the NIST standard are a bit out-dated and arbitrary, so we altered some minor things to make more sense. Differences are outlined in the source code.) We also took steps to allow us to break up the g-code parser into distinct separate tasks, which is key for some future development ideas and improvements.
- - **Independent Acceleration and Velocity Settings:** Each axes may be defined with unique acceleration and velocity parameters and Grbl will automagically calculate the maximum acceleration and velocity through a path depending on the direction traveled. This is very useful for machines that have very different axes properties, like the ShapeOko's z-axis.
- - **Soft Limits:** Checks if any motion command exceeds workspace limits before executing it, and alarms out, if detected. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out. NOTE: This still requires limit switches for homing so Grbl knows where the machine origin is, and the new max axis travel settings configured correctly for the machine.
- - **Probing:** The G38.2 straight probe and G43.1/49 tool offset g-code commands are now supported. A simple probe switch must be connected to the Uno analog pin 5 (normally-open to ground). Grbl will report the probe position back to the user when the probing cycle detects a pin state change.
- - **Tool Length Offsets:** Probing doesn't make sense without tool length offsets(TLO), so we added it! The G43.1 dynamic TLO (described by linuxcnc.org) and G49 TLO cancel commands are now supported. G43.1 dynamic TLO works like the normal G43 TLO(NOT SUPPORTED) but requires an additional axis word with the offset value attached. We did this so Grbl does not have to track and maintain a tool offset database in its memory. Perhaps in the future, we will support a tool database, but not for this version.
- - **Improved Arc Performance:** The larger the arc radius, the faster Grbl will trace it! We are now defining arcs in terms of arc chordal tolerance, rather than a fixed segment length. This automatically scales the arc segment length such that maximum radial error of the segment from the true arc is never more than the chordal tolerance value of a super-accurate default of 0.002 mm.
- - **CPU Pin Mapping:** In an effort for Grbl to be compatible with other AVR architectures, such as the 1280 or 2560, a new cpu_map.h pin configuration file has been created to allow Grbl to be compiled for them. This is currently user supported, so your mileage may vary. If you run across a bug, please let us know or better send us a fix! Thanks in advance!
- - **New Grbl SIMULATOR! (by @jgeisler and @ashelly):** A completely independent wrapper of the Grbl main source code that may be compiled as an executable on a computer. No Arduino required. Simply simulates the responses of Grbl as if it was on an Arduino. May be used for many things: checking out how Grbl works, pre-process moves for GUI graphics, debugging of new features, etc. Much left to do, but potentially very powerful, as the dummy AVR variables can be written to output anything you need.
- - **Configurable Real-time Status Reporting:** Users can now customize the type of real-time data Grbl reports back when they issue a '?' status report. This includes data such as: machine position, work position, planner buffer usage, serial RX buffer usage.
- - **Updated Homing Routine:** Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. But, the behavior may be changed by a compile-time option though. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking.
- - **Optional Limit Pin Sharing:** Limit switches can be combined to share the same pins to free up precious I/O pins for other purposes. When combined, users must adjust the homing cycle mask in config.h to not home the axes on a shared pin at the same time. Don't worry; hard limits and the homing cycle still work just like they did before.
- - **Optional Variable Spindle Speed Output:** Available only as a compile-time option through the config.h file. Enables PWM output for 'S' g-code commands. Enabling this feature will swap the Z-limit D11 pin and spindle enable D12 pin to access the hardware PWM on pin D12. The Z-limit pin, now on D12, should work just as it did before.
- - **Additional Compile-Time Feature Options:** Line number tracking, real-time feed rate reporting.
- - **SLATED FOR v1.0 DEVELOPMENT** Jogging controls and feedrate/spindle/coolant overrides. (In v0.9, the framework for feedrate overrides are in-place, only the minor details to complete it have yet to be installed.)
-
--
-```
-List of Supported G-Codes in Grbl v0.9
- - Non-Modal Commands: G4, G10 L2, G10 L20, G28, G30, G28.1, G30.1, G53, G92, G92.1
- - Motion Modes: G0, G1, G2, G3, G38.1, G80
- - Feed Rate Modes: G93, G94
- - Unit Modes: G20, G21
- - Distance Modes: G90, G91
- - Plane Select Modes: G17, G18, G19
- - Tool Length Offset Modes: G43.1, G49
- - Coordinate System Modes: G54, G55, G56, G57, G58, G59
- - Program Flow: M0, M1, M2, M30*
- - Coolant Control: M7*, M8, M9
- - Spindle Control: M3, M4, M5
-```
-
--------------
-Grbl is an open-source project and fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you!
-
-[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EBQWAWQAAT878)
+## Grbl Firmware for OpenPnP
+
+
+
+### Features
+ - Dual Y axis
+ - PWM output to control a Remote Control (RC) Servo for Drag Feeder
+ - Support for two actuators
+ - Forked from GRBL_VERSION "1.1h" GRBL_VERSION_BUILD "20190830"
+
+
+### A Machine in Action
+https://user-images.githubusercontent.com/26599790/154530634-a4084f31-4077-454d-b018-5f61cd336622.mp4
+
+Watch the complete video at https://youtu.be/EM1xYVEcOd8
+
+### Recommended Wiring Diagram
+
+
+
+
+
+### Repurposed GCodes
+Function | Command
+----|-----
+RC Servo Power | M3 - On, M5 - Off
+RC Servo Position | S30 - raise, S90 - lower
+Vacuum | M8 - On, M9 - Off
+LED | M7 - On, M10 - Off
+
+### GCode Driver Commands
+OpenPnP *Issues and Solutions* recognises this firmware and auto populates GCode commands. The following is only for reference.
+
+Setting | GCode
+----|-----
+CONNECT_COMMAND | G21 ; Set millimeters mode
G90 ; Set absolute positioning mode
+HOME_COMMAND | $H;
+MOVE_TO_COMMAND | G1 {X:X%.4f} {Y:Y%.4f} {Z:Z%.4f} {C:C%.4f} {FeedRate:F:%.2f}
+MOVE_TO_COMPLETE_COMMAND |G4 P0; Wait for moves to complete before returning
+SET_GLOBAL_OFFSETS_COMMAND | G92 {X:X%.4f} {Y:Y%.4f} {Z:Z%.4f} {C:C%.4f} ; reset coordinates_
+COMMAND_CONFIRM_REGEX | ^ok.*
+POSITION_REPORT_REGEX | ^.\*(?-?\d+\.\d+) (?-?\d+\.\d+) (?-?\d+\.\d+) (?-?\d+\.\d+).*
+COMMAND_ERROR_REGEX | ^Crash.*
+
+#### Credits
+
+Grbl is an open-source project fueled by the free-time of our intrepid administrators and altruistic users. If you'd like to donate, all proceeds will be used to help fund supporting hardware and testing equipment. Thank you!
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CUGXJHXA36BYW)
+
+A fourth axis for grbl was added by Bob Beattie in 2014
+
+It was updated to version 1.1h and made I&S compatible by Ravi Ganesh in 2021
+
diff --git a/config.h b/config.h
index adfd47cb7..1efc35b4e 100644
--- a/config.h
+++ b/config.h
@@ -1,8 +1,9 @@
/*
config.h - compile time configuration
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2013-2014 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
@@ -17,13 +18,7 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2013 Sungeun K. Jeon
-*/
-
+
// 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.
@@ -32,29 +27,60 @@
#ifndef config_h
#define config_h
-#include "system.h"
+#include "grbl.h" // For Arduino IDE compatibility.
-// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
-#define DEFAULTS_OPENPNP
+// Define CPU pin map and default settings.
+// NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only
+// one configuration file by placing their specific defaults and pin map at the bottom of this file.
+// If doing so, simply comment out these two defines and see instructions below.
+#define DEFAULTS_GENERIC
+#define CPU_MAP_ATMEGA328P // Arduino Uno CPU
// Serial baud rate
-#define BAUD_RATE 9600
-
-// Default cpu mappings. Grbl officially supports the Arduino Uno only. Other processor types
-// may exist from user-supplied templates or directly user-defined in cpu_map.h
-#define CPU_MAP_ATMEGA2560_4_AXES // Arduino Mega 2560
+// #define BAUD_RATE 230400
+#define BAUD_RATE 115200
-// Define runtime command special characters. These characters are 'picked-off' directly from the
+// 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_RESET 0x18 // ctrl-x.
#define CMD_STATUS_REPORT '?'
-#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
-#define CMD_RESET 0x18 // ctrl-x.
+#define CMD_FEED_HOLD '!'
+
+// 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_JOG_CANCEL 0x85
+#define CMD_DEBUG_REPORT 0x86 // 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
@@ -63,30 +89,42 @@
// 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< 3us, and, when added with the
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
@@ -202,49 +425,42 @@
// #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
-
-// Toggles XON/XOFF software flow control for serial communications. Not officially supported
-// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware
-// on these chips do not support XON/XOFF flow control characters and the intermediate buffer
-// in the chips cause latency and overflow problems with standard terminal programs. However,
-// using specifically-programmed UI's to manage this latency problem has been confirmed to work.
-// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard
-// terminal programs since their firmware correctly manage these XON/XOFF characters. In any
-// case, please report any successes to grbl administrators!
-// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
+// 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 100 // (1-254)
// 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
@@ -255,18 +471,237 @@
// 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.
-// ---------------------------------------------------------------------------------------
+// Configures the position after a probing cycle during Grbl's check mode. Disabled sets
+// the position to the probe target, when enabled sets the position to the start position.
+// #define SET_CHECK_MODE_PROBE_TO_START // 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
+// 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
+// triggering a hard limit when the machine disengages from the switch.
+// NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled.
+// #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
+// 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
+// the limit switch.
+// NOTE: Both of these values must be greater than 1.0 to ensure proper function.
+// #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: If disabled and to ensure Grbl can never alter the build info line, you'll also need to enable
+// the SETTING_RESTORE_ALL macro above and remove SETTINGS_RESTORE_BUILD_INFO from the mask.
+// 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.
+
+// By default, Grbl disables feed rate overrides for all G38.x probe cycle commands. Although this
+// may be different than some pro-class machine control, it's arguable that it should be this way.
+// Most probe sensors produce different levels of error that is dependent on rate of speed. By
+// keeping probing cycles to their programmed feed rates, the probe sensor should be a lot more
+// repeatable. If needed, you can disable this behavior by uncommenting the define below.
+// #define ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES // Default disabled. Uncomment to enable.
+
+// 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
+// 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
+// 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
+// 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
+// positive direction.
+// #define PARKING_ENABLE // Default disabled. Uncomment to enable
+
+// Configure options for the parking motion, if enabled.
+#define PARKING_AXIS Z_AXIS // Define which axis that performs the parking motion
+#define PARKING_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
+#define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min.
+#define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min.
+#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
+ // Must be positive value or equal to zero.
+
+// Enables a special set of M-code commands that enables and disables the parking motion.
+// These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable.
+// The command is modal and will be set after a planner sync. Since it is g-code, it is
+// executed in sync with g-code commands. It is not a real-time command.
+// NOTE: PARKING_ENABLE is required. By default, M56 is active upon initialization. Use
+// DEACTIVATE_PARKING_UPON_INIT to set M56 P0 as the power-up default.
+// #define ENABLE_PARKING_OVERRIDE_CONTROL // Default disabled. Uncomment to enable
+// #define DEACTIVATE_PARKING_UPON_INIT // Default disabled. Uncomment to enable.
+
+// This option will automatically disable the laser during a feed hold by invoking a spindle stop
+// override immediately after coming to a stop. However, this also means that the laser still may
+// be reenabled by disabling the spindle stop override, if needed. This is purely a safety feature
+// to ensure the laser doesn't inadvertently remain powered while at a stop and cause a fire.
+#define DISABLE_LASER_DURING_HOLD // Default enabled. Comment to disable.
+
+// This feature alters the spindle PWM/speed to a nonlinear output with a simple piecewise linear
+// curve. Useful for spindles that don't produce the right RPM from Grbl's standard spindle PWM
+// linear model. Requires a solution by the 'fit_nonlinear_spindle.py' script in the /doc/script
+// folder of the repo. See file comments on how to gather spindle data and run the script to
+// generate a solution.
+// #define ENABLE_PIECEWISE_LINEAR_SPINDLE // Default disabled. Uncomment to enable.
+
+// N_PIECES, RPM_MAX, RPM_MIN, RPM_POINTxx, and RPM_LINE_XX constants are all set and given by
+// the 'fit_nonlinear_spindle.py' script solution. Used only when ENABLE_PIECEWISE_LINEAR_SPINDLE
+// is enabled. Make sure the constant values are exactly the same as the script solution.
+// NOTE: When N_PIECES < 4, unused RPM_LINE and RPM_POINT defines are not required and omitted.
+#define N_PIECES 4 // Integer (1-4). Number of piecewise lines used in script solution.
+#define RPM_MAX 11686.4 // Max RPM of model. $30 > RPM_MAX will be limited to RPM_MAX.
+#define RPM_MIN 202.5 // Min RPM of model. $31 < RPM_MIN will be limited to RPM_MIN.
+#define RPM_POINT12 6145.4 // Used N_PIECES >=2. Junction point between lines 1 and 2.
+#define RPM_POINT23 9627.8 // Used N_PIECES >=3. Junction point between lines 2 and 3.
+#define RPM_POINT34 10813.9 // Used N_PIECES = 4. Junction point between lines 3 and 4.
+#define RPM_LINE_A1 3.197101e-03 // Used N_PIECES >=1. A and B constants of line 1.
+#define RPM_LINE_B1 -3.526076e-1
+#define RPM_LINE_A2 1.722950e-2 // Used N_PIECES >=2. A and B constants of line 2.
+#define RPM_LINE_B2 8.588176e+01
+#define RPM_LINE_A3 5.901518e-02 // Used N_PIECES >=3. A and B constants of line 3.
+#define RPM_LINE_B3 4.881851e+02
+#define RPM_LINE_A4 1.203413e-01 // Used N_PIECES = 4. A and B constants of line 4.
+#define RPM_LINE_B4 1.151360e+03
+
+/* ---------------------------------------------------------------------------------------
+ This optional dual axis feature is primarily for the homing cycle to locate two sides of
+ a dual-motor gantry independently, i.e. self-squaring. This requires an additional limit
+ switch for the cloned motor. To self square, both limit switches on the cloned axis must
+ be physically positioned to trigger when the gantry is square. Highly recommend keeping
+ the motors always enabled to ensure the gantry stays square with the $1=255 setting.
+
+ For Grbl on the Arduino Uno, the cloned axis limit switch must to be shared with and
+ wired with z-axis limit pin due to the lack of available pins. The homing cycle must home
+ the z-axis and cloned axis in different cycles, which is already the default config.
+
+ The dual axis feature works by cloning an axis step output onto another pair of step
+ and direction pins. The step pulse and direction of the cloned motor can be set
+ independently of the main axis motor. However to save precious flash and memory, this
+ dual axis feature must share the same settings (step/mm, max speed, acceleration) as the
+ parent motor. This is NOT a feature for an independent fourth axis. Only a motor clone.
+
+ WARNING: Make sure to test the directions of your dual axis motors! They must be setup
+ to move the same direction BEFORE running your first homing cycle or any long motion!
+ Motors moving in opposite directions can cause serious damage to your machine! Use this
+ dual axis feature at your own risk.
+*/
+// NOTE: This feature requires approximately 400 bytes of flash. Certain configurations can
+// run out of flash to fit on an Arduino 328p/Uno. Only X and Y axes are supported. Variable
+// spindle/laser mode IS supported, but only for one config option. Core XY, spindle direction
+// pin, and M7 mist coolant are disabled/not supported.
+// #define ENABLE_DUAL_AXIS // Default disabled. Uncomment to enable.
+
+// Select the one axis to mirror another motor. Only X and Y axis is supported at this time.
+#define DUAL_AXIS_SELECT Y_AXIS // Must be either X_AXIS or Y_AXIS
+
+// To prevent the homing cycle from racking the dual axis, when one limit triggers before the
+// other due to switch failure or noise, the homing cycle will automatically abort if the second
+// motor's limit switch does not trigger within the three distance parameters defined below.
+// Axis length percent will automatically compute a fail distance as a percentage of the max
+// travel of the other non-dual axis, i.e. if dual axis select is X_AXIS at 5.0%, then the fail
+// distance will be computed as 5.0% of y-axis max travel. Fail distance max and min are the
+// limits of how far or little a valid fail distance is.
+#define DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT 5.0 // Float (percent)
+#define DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX 25.0 // Float (mm)
+#define DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN 2.5 // Float (mm)
+
+// Dual axis pin configuration currently supports two shields. Uncomment the shield you want,
+// and comment out the other one(s).
+// NOTE: Protoneer CNC Shield v3.51 has A.STP and A.DIR wired to pins A4 and A3 respectively.
+// The variable spindle (i.e. laser mode) build option works and may be enabled or disabled.
+// Coolant pin A3 is moved to D13, replacing spindle direction.
+#define DUAL_AXIS_CONFIG_PROTONEER_V3_51 // Uncomment to select. Comment other configs.
+
+// NOTE: Arduino CNC Shield Clone (Originally Protoneer v3.0) has A.STP and A.DIR wired to
+// D12 and D13, respectively. With the limit pins and stepper enable pin on this same port,
+// the spindle enable pin had to be moved and spindle direction pin deleted. The spindle
+// enable pin now resides on A3, replacing coolant enable. Coolant enable is bumped over to
+// pin A4. Spindle enable is used far more and this pin setup helps facilitate users to
+// integrate this feature without arguably too much work.
+// Variable spindle (i.e. laser mode) does NOT work with this shield as configured. While
+// variable spindle technically can work with this shield, it requires too many changes for
+// most user setups to accomodate. It would best be implemented by sharing all limit switches
+// on pins D9/D10 (as [X1,Z]/[X2,Y] or [X,Y2]/[Y1,Z]), home each axis independently, and
+// updating lots of code to ensure everything is running correctly.
+// #define DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE // Uncomment to select. Comment other configs.
+
+
+/* ---------------------------------------------------------------------------------------
+ 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
+ below.
+*/
-// TODO: Install compile-time option to send numeric status codes rather than strings.
+ #undef CPU_MAP_ATMEGA328P // Regular
-// ---------------------------------------------------------------------------------------
-// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
+ #define CPU_MAP_ATMEGA328P_4_AXIS // For OpenPnP
+ #define VARIABLE_SPINDLE // Servo uses PWM
+ //#define DUAL_AXIS_SELECT Y_AXIS // selected above
+ #define USE_SPINDLE_DIR_AS_ENABLE_PIN
+ #define ENABLE_M7 // COOLANT_MIST (for Bottom LED, M7 On, M10 Off)
-// #if (ISR_TICKS_PER_ACCELERATION_TICK > 255)
-// #error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow.
-// #endif
+// my defines
+ #define VISUAL_HOMING // $H enables softlimits until next reboot. I dont use homing or limit switches.
+ // I am using $24, $25 and $27. These are homing cycle
+ // related setting and I never used Homing with Limit switches. For CNC I used Dial guage
+ // on Ball Screw. For PnP registering with Camera is awesome.
+ // I set Machine Home by cross hairing manual feeder origin and hence these setting variables.
+
+#define M114M115 // as required by OpenPnP
-// ---------------------------------------------------------------------------------------
+// Paste default settings definitions here.
#endif
diff --git a/coolant_control.c b/coolant_control.c
index 9f629ce8c..1eebfc0c7 100644
--- a/coolant_control.c
+++ b/coolant_control.c
@@ -1,8 +1,8 @@
/*
coolant_control.c - coolant control methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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,17 +16,14 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
-*/
+*/
-#include "system.h"
-#include "coolant_control.h"
-#include "protocol.h"
-#include "gcode.h"
+#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
@@ -34,30 +31,96 @@ void coolant_init()
}
-void coolant_stop()
+// Returns current coolant output state. Overrides may alter it from programmed state.
+uint8_t coolant_get_state()
{
- COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
+ uint8_t cl_state = COOLANT_STATE_DISABLE;
+ #ifdef INVERT_COOLANT_FLOOD_PIN
+ if (bit_isfalse(COOLANT_FLOOD_PORT,(1 << COOLANT_FLOOD_BIT))) {
+ #else
+ if (bit_istrue(COOLANT_FLOOD_PORT,(1 << COOLANT_FLOOD_BIT))) {
+ #endif
+ cl_state |= COOLANT_STATE_FLOOD;
+ }
#ifdef ENABLE_M7
- COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
+ #ifdef INVERT_COOLANT_MIST_PIN
+ if (bit_isfalse(COOLANT_MIST_PORT,(1 << COOLANT_MIST_BIT))) {
+ #else
+ if (bit_istrue(COOLANT_MIST_PORT,(1 << COOLANT_MIST_BIT))) {
+ #endif
+ cl_state |= COOLANT_STATE_MIST;
+ }
#endif
+ return(cl_state);
}
-void coolant_run(uint8_t mode)
+// Directly called by coolant_init(), coolant_set_state(), and mc_reset(), which can be at
+// an interrupt-level. No report flag set, but only called by routines that don't need it.
+void coolant_stop()
{
- if (sys.state == STATE_CHECK_MODE) { return; }
-
- protocol_auto_cycle_start(); //temp fix for M8 lockup
- protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
- if (mode == COOLANT_FLOOD_ENABLE) {
+ #ifdef INVERT_COOLANT_FLOOD_PIN
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
-
- #ifdef ENABLE_M7
- } else if (mode == COOLANT_MIST_ENABLE) {
+ #else
+ COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
+ #endif
+ #ifdef ENABLE_M7
+ #ifdef INVERT_COOLANT_MIST_PIN
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
+ #else
+ COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
+ #endif
#endif
+}
- } else {
- coolant_stop();
- }
+
+// Main program only. Immediately sets flood coolant running state and also mist coolant,
+// if enabled. Also sets a flag to report an update to a coolant state.
+// Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code
+// parser program end, and g-code parser coolant_sync().
+void coolant_set_state(uint8_t mode)
+{
+ if (sys.abort) { return; } // Block during abort.
+
+ 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 {
+ #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
+
+ sys.report_ovr_counter = 0; // Set to report change immediately
+}
+
+
+// G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails
+// if an abort or check-mode is active.
+void coolant_sync(uint8_t mode)
+{
+ if (sys.state == STATE_CHECK_MODE) { return; }
+ protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
+ coolant_set_state(mode);
}
diff --git a/coolant_control.h b/coolant_control.h
index db3c71cd8..49b85f000 100644
--- a/coolant_control.h
+++ b/coolant_control.h
@@ -1,8 +1,8 @@
/*
coolant_control.h - spindle control methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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,11 +19,29 @@
*/
#ifndef coolant_control_h
-#define coolant_control_h
+#define coolant_control_h
+#define COOLANT_NO_SYNC false
+#define COOLANT_FORCE_SYNC true
+#define COOLANT_STATE_DISABLE 0 // Must be zero
+#define COOLANT_STATE_FLOOD PL_COND_FLAG_COOLANT_FLOOD
+#define COOLANT_STATE_MIST PL_COND_FLAG_COOLANT_MIST
+
+
+// Initializes coolant control pins.
void coolant_init();
+
+// Returns current coolant output state. Overrides may alter it from programmed state.
+uint8_t coolant_get_state();
+
+// Immediately disables coolant pins.
void coolant_stop();
-void coolant_run(uint8_t mode);
-#endif
\ No newline at end of file
+// Sets the coolant pins according to state specified.
+void coolant_set_state(uint8_t mode);
+
+// G-code parser entry-point for setting coolant states. Checks for and executes additional conditions.
+void coolant_sync(uint8_t mode);
+
+#endif
diff --git a/cpu_map.h b/cpu_map.h
index 4f7dd9882..21d1181c3 100644
--- a/cpu_map.h
+++ b/cpu_map.h
@@ -1,491 +1,370 @@
-/*
- cpu_map.h - CPU and pin mapping configuration file
- Part of Grbl v0.9
-
- Copyright (c) 2012-2014 Sungeun K. Jeon
-
- Grbl is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Grbl is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Grbl. If not, see .
-*/
-
-/* The cpu_map.h file serves as a central pin mapping settings file for different processor
- types, i.e. AVR 328p or AVR Mega 2560. Grbl officially supports the Arduino Uno, but the
- other supplied pin mappings are supplied by users, so your results may vary. */
-
-// NOTE: This is still a work in progress. We are still centralizing the configurations to
-// this file, so your success may vary for other CPUs.
-
-#ifndef cpu_map_h
-#define cpu_map_h
-
-
-//----------------------------------------------------------------------------------------
-
-#ifdef CPU_MAP_ATMEGA328P_TRADITIONAL // (Arduino Uno) Officially supported by Grbl.
-
- // Define serial port pins and interrupt vectors.
- #define SERIAL_RX USART_RX_vect
- #define SERIAL_UDRE USART_UDRE_vect
-
- // Define step pulse output pins. NOTE: All step bit pins must be on the same port.
- #define STEP_DDR DDRD
- #define STEP_PORT PORTD
- #define X_STEP_BIT 2 // Uno Digital Pin 2
- #define Y_STEP_BIT 3 // Uno Digital Pin 3
- #define Z_STEP_BIT 4 // Uno Digital Pin 4
- #define STEP_MASK ((1<.
+*/
+
+/* 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. */
+
+
+#ifndef cpu_map_h
+#define cpu_map_h
+
+
+#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl.
+
+ // Define serial port pins and interrupt vectors.
+ #define SERIAL_RX USART_RX_vect
+ #define SERIAL_UDRE USART_UDRE_vect
+
+ // Define step pulse output pins. NOTE: All step bit pins must be on the same port.
+ #define STEP_DDR DDRD
+ #define STEP_PORT PORTD
+ #define X_STEP_BIT 2 // Uno Digital Pin 2
+ #define Y_STEP_BIT 3 // Uno Digital Pin 3
+ #define Z_STEP_BIT 4 // Uno Digital Pin 4
+ #define STEP_MASK ((1< 62.5kHz
+ // #define SPINDLE_TCCRB_INIT_MASK (1< 7.8kHz (Used in v0.9)
+ // #define SPINDLE_TCCRB_INIT_MASK ((1< 1.96kHz
+ #define SPINDLE_TCCRB_INIT_MASK (1< 0.98kHz (J-tech laser)
+
+ // 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
+
+ #else
+
+ // Dual axis feature requires an independent step pulse pin to operate. The independent direction pin is not
+ // absolutely necessary but facilitates easy direction inverting with a Grbl $$ setting. These pins replace
+ // the spindle direction and optional coolant mist pins.
+
+ #ifdef DUAL_AXIS_CONFIG_PROTONEER_V3_51
+ // NOTE: Step pulse and direction pins may be on any port and output pin.
+ #define STEP_DDR_DUAL DDRC
+ #define STEP_PORT_DUAL PORTC
+ #define DUAL_STEP_BIT 4 // Uno Analog Pin 4
+ #define STEP_MASK_DUAL ((1< 62.5kHz
+ // #define SPINDLE_TCCRB_INIT_MASK (1< 7.8kHz (Used in v0.9)
+ // #define SPINDLE_TCCRB_INIT_MASK ((1< 1.96kHz
+ #define SPINDLE_TCCRB_INIT_MASK (1< 0.98kHz (J-tech laser)
+
+ // 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
+
+ // NOTE: Variable spindle not supported with this shield.
+ #ifdef DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE
+ // NOTE: Step pulse and direction pins may be on any port and output pin.
+ #define STEP_DDR_DUAL DDRB
+ #define STEP_PORT_DUAL PORTB
+ #define DUAL_STEP_BIT 4 // Uno Digital Pin 12
+ #define STEP_MASK_DUAL ((1< 62.5kHz
+ // #define SPINDLE_TCCRB_INIT_MASK (1< 7.8kHz (Used in v0.9)
+ // #define SPINDLE_TCCRB_INIT_MASK ((1< 1.96kHz
+ //#define SPINDLE_TCCRB_INIT_MASK (1< 0.98kHz (J-tech laser)
+ #define SPINDLE_TCCRB_INIT_MASK ((1< 61Hz (Servo Ctrl)
+
+
+ // Define coolant enable output pins.
+ // NOTE: COOLANT_FLOOD is used for Vacum and operated by M8, M9. COOLANT_MIST -> Bottom LED M7, M10. See Arduino Wriing.jpg
+ #define COOLANT_FLOOD_DDR DDRC
+ #define COOLANT_FLOOD_PORT PORTC
+ #define COOLANT_FLOOD_BIT 4 // Uno Analog Pin 4
+
+ #define COOLANT_MIST_DDR DDRC
+ #define COOLANT_MIST_PORT PORTC
+ #define COOLANT_MIST_BIT 5 // Uno Analog Pin 5
+
+ // Define probe switch input pin.
+ #define PROBE_DDR DDRC
+ #define PROBE_PIN PINC
+ #define PROBE_PORT PORTC
+ #define PROBE_BIT 6 // not used and hence mapped to RESET/PCINT14 – Port C, Bit 6
+ #define PROBE_MASK (1<.
-*/
-
-/* The defaults.h file serves as a central default settings file for different machine
- types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
- here are supplied by users, so your results may vary. However, this should give you
- a good starting point as you get to know your machine and tweak the settings for your
- our nefarious needs. */
-
-#ifndef defaults_h
-#define defaults_h
-
-#ifdef DEFAULTS_GENERIC
- // Grbl generic default settings. Should work across different machines.
- #define DEFAULT_X_STEPS_PER_MM 250.0
- #define DEFAULT_Y_STEPS_PER_MM 250.0
- #define DEFAULT_Z_STEPS_PER_MM 250.0
- #define DEFAULT_X_MAX_RATE 500.0 // mm/min
- #define DEFAULT_Y_MAX_RATE 500.0 // mm/min
- #define DEFAULT_Z_MAX_RATE 500.0 // mm/min
- #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
- #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
- #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
- #define DEFAULT_X_MAX_TRAVEL 200.0 // mm
- #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm
- #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
- #define DEFAULT_STEP_PULSE_MICROSECONDS 10
- #define DEFAULT_STEPPING_INVERT_MASK 0
- #define DEFAULT_DIRECTION_INVERT_MASK ((1<.
+*/
+
+/* 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
+ 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.
+ NOTE: Ensure one and only one of these DEFAULTS_XXX values is defined in config.h */
+
+#ifndef defaults_h
+
+#ifdef DEFAULTS_GENERIC
+ // Grbl generic default settings. Should work across different machines.
+ #define DEFAULT_X_STEPS_PER_MM 44.400
+ #define DEFAULT_Y_STEPS_PER_MM 44.400
+ #define DEFAULT_Z_STEPS_PER_MM 264.550
+ #define DEFAULT_C_STEPS_PER_MM 8.896
+ #define DEFAULT_X_MAX_RATE 10000.0 // mm/min
+ #define DEFAULT_Y_MAX_RATE 10000.0 // mm/min
+ #define DEFAULT_Z_MAX_RATE 1600.0 // mm/min
+ #define DEFAULT_C_MAX_RATE 40000.000 // mm/min
+ #define DEFAULT_X_ACCELERATION (500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
+ #define DEFAULT_Y_ACCELERATION (500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
+ #define DEFAULT_Z_ACCELERATION (1000.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
+ #define DEFAULT_C_ACCELERATION (2000.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
+ #define DEFAULT_X_MAX_TRAVEL 310.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_Y_MAX_TRAVEL 330.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_Z_MAX_TRAVEL 30.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_C_MAX_TRAVEL 0.0 // endless
+ #define DEFAULT_SPINDLE_RPM_MAX 1000.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 4
+ #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
+ #define DEFAULT_STATUS_REPORT_MASK 1 // MPos enabled
+ #define DEFAULT_JUNCTION_DEVIATION 0.01 // 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_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
+#ifdef VISUAL_HOMING
+ #define DEFAULT_HOMING_FEED_RATE 152.0 // Machine X0 to Head Home X(mm)
+ #define DEFAULT_HOMING_SEEK_RATE 20.0 // Machine Y0 to Head Home Y(mm)
+ #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // NA
+ #define DEFAULT_HOMING_PULLOFF 0.0 // Machine Z0 to Head Home Z(mm)
+#else
+ #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
+ #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min
+ #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
+ #define DEFAULT_HOMING_PULLOFF 1.0 // mm
+#endif // VISUAL_HOMING
+#endif
+
+#ifdef DEFAULTS_SHERLINE_5400
+ // Description: Sherline 5400 mill with three NEMA 23 Keling KL23H256-21-8B 185 oz-in stepper motors,
+ // driven by three Pololu A4988 stepper drivers with a 30V, 6A power supply at 1.5A per winding.
+ #define MICROSTEPS 2
+ #define STEPS_PER_REV 200.0
+ #define MM_PER_REV (0.050*MM_PER_INCH) // 0.050 inch/rev leadscrew
+ #define DEFAULT_X_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
+ #define DEFAULT_Y_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
+ #define DEFAULT_Z_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
+ #define DEFAULT_X_MAX_RATE 635.0 // mm/min (25 ipm)
+ #define DEFAULT_Y_MAX_RATE 635.0 // mm/min
+ #define DEFAULT_Z_MAX_RATE 635.0 // mm/min
+ #define DEFAULT_X_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
+ #define DEFAULT_Y_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
+ #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
+ #define DEFAULT_X_MAX_TRAVEL 225.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_Y_MAX_TRAVEL 125.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm NOTE: Must be a positive value.
+ #define DEFAULT_SPINDLE_RPM_MAX 2800.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<.
*/
#ifndef eeprom_h
diff --git a/gcode.c b/gcode.c
index 78f039803..8cfc33d0d 100644
--- a/gcode.c
+++ b/gcode.c
@@ -1,10 +1,10 @@
/*
gcode.c - rs274/ngc parser.
- Part of Grbl v0.9
+ Part of Grbl
+
+ Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
+ Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012-2014 Sungeun K. Jeon
- Copyright (c) 2014 Bob Beattie
-
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,30 +18,17 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "settings.h"
-#include "protocol.h"
-#include "gcode.h"
-#include "motion_control.h"
-#include "spindle_control.h"
-#include "coolant_control.h"
-#include "probe.h"
-#include "report.h"
+
+#include "grbl.h"
// NOTE: Max line number is defined by the g-code standard to be 99999. It seems to be an
// arbitrary value, and some GUIs may require more. So we increased it based on a max safe
// value when converting a float (7.2 digit precision)s to an integer.
-#define MAX_LINE_NUMBER 9999999
+#define MAX_LINE_NUMBER 10000000
+#define MAX_TOOL_NUMBER 255 // Limited by max unsigned 8-bit value
#define AXIS_COMMAND_NONE 0
-#define AXIS_COMMAND_NON_MODAL 1
+#define AXIS_COMMAND_NON_MODAL 1
#define AXIS_COMMAND_MOTION_MODE 2
#define AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 // *Undefined but required
@@ -52,83 +39,83 @@ parser_block_t gc_block;
#define FAIL(status) return(status);
-void gc_init()
+void gc_init()
{
- memset(&gc_state, 0, sizeof(gc_state));
-
+ memset(&gc_state, 0, sizeof(parser_state_t));
+
// Load default G54 coordinate system.
- if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- }
+ if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) {
+ report_status_message(STATUS_SETTING_READ_FAIL);
+ }
}
// Sets g-code parser position in mm. Input in steps. Called by the system abort and hard
// limit pull-off routines.
-void gc_sync_position()
+void gc_sync_position()
{
- uint8_t i;
- for (i=0; i 255, variable type must be changed to uint16_t.
-
+ uint16_t mantissa = 0;
+ if (gc_parser_flags & GC_PARSER_JOG_MOTION) { char_counter = 3; } // Start parsing after `$J=`
+ else { char_counter = 0; }
while (line[char_counter] != 0) { // Loop until no more g-code words in line.
-
+
// Import the next g-code word, expecting a letter followed by a value. Otherwise, error out.
letter = line[char_counter];
if((letter < 'A') || (letter > 'Z')) { FAIL(STATUS_EXPECTED_COMMAND_LETTER); } // [Expected word letter]
@@ -136,27 +123,27 @@ uint8_t gc_execute_line(char *line)
if (!read_float(line, &char_counter, &value)) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // [Expected word value]
// Convert values to smaller uint8 significand and mantissa values for parsing this word.
- // NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more
+ // NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more
// accurate than the NIST gcode requirement of x10 when used for commands, but not quite
// accurate enough for value words that require integers to within 0.0001. This should be
- // a good enough comprimise and catch most all non-integer errors. To make it compliant,
+ // a good enough comprimise and catch most all non-integer errors. To make it compliant,
// we would simply need to change the mantissa to int16, but this add compiled flash space.
- // Maybe update this later.
+ // Maybe update this later.
int_value = trunc(value);
mantissa = round(100*(value - int_value)); // Compute mantissa for Gxx.x commands.
- // NOTE: Rounding must be used to catch small floating point errors.
+ // NOTE: Rounding must be used to catch small floating point errors.
// Check if the g-code word is supported or errors due to modal group violations or has
// been repeated in the g-code block. If ok, update the command or record its value.
switch(letter) {
-
+
/* 'G' and 'M' Command Words: Parse commands and check for modal group violations.
NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */
-
+
case 'G':
// Determine 'G' command and its modal group
switch(int_value) {
- case 10: case 28: case 30: case 92:
+ case 10: case 28: case 30: case 92:
// Check for G10/28/30/92 being called with G0/1/2/3/38 on same block.
// * G43.1 is also an axis command but is not explicitly defined this way.
if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1
@@ -164,168 +151,163 @@ uint8_t gc_execute_line(char *line)
axis_command = AXIS_COMMAND_NON_MODAL;
}
// No break. Continues to next line.
- case 4: case 53:
- word_bit = MODAL_GROUP_G0;
- switch(int_value) {
- case 4: gc_block.non_modal_command = NON_MODAL_DWELL; break; // G4
- case 10: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_DATA; break; // G10
- case 28:
- switch(mantissa) {
- case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_0; break; // G28
- case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_0; break; // G28.1
- default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G28.x command]
- }
- mantissa = 0; // Set to zero to indicate valid non-integer G command.
- break;
- case 30:
- switch(mantissa) {
- case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_1; break; // G30
- case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_1; break; // G30.1
- default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G30.x command]
- }
- mantissa = 0; // Set to zero to indicate valid non-integer G command.
- break;
- case 53: gc_block.non_modal_command = NON_MODAL_ABSOLUTE_OVERRIDE; break; // G53
- case 92:
- switch(mantissa) {
- case 0: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_OFFSET; break; // G92
- case 10: gc_block.non_modal_command = NON_MODAL_RESET_COORDINATE_OFFSET; break; // G92.1
- default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G92.x command]
- }
- mantissa = 0; // Set to zero to indicate valid non-integer G command.
- break;
- }
+ case 4: case 53:
+ word_bit = MODAL_GROUP_G0;
+ gc_block.non_modal_command = int_value;
+ if ((int_value == 28) || (int_value == 30) || (int_value == 92)) {
+ if (!((mantissa == 0) || (mantissa == 10))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); }
+ gc_block.non_modal_command += mantissa;
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ }
break;
- case 0: case 1: case 2: case 3: case 38:
+ case 0: case 1: case 2: case 3: case 38:
// Check for G0/1/2/3/38 being called with G10/28/30/92 on same block.
// * G43.1 is also an axis command but is not explicitly defined this way.
if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
- axis_command = AXIS_COMMAND_MOTION_MODE;
+ axis_command = AXIS_COMMAND_MOTION_MODE;
// No break. Continues to next line.
- case 80:
- word_bit = MODAL_GROUP_G1;
- switch(int_value) {
- case 0: gc_block.modal.motion = MOTION_MODE_SEEK; break; // G0
- case 1: gc_block.modal.motion = MOTION_MODE_LINEAR; break; // G1
- case 2: gc_block.modal.motion = MOTION_MODE_CW_ARC; break; // G2
- case 3: gc_block.modal.motion = MOTION_MODE_CCW_ARC; break; // G3
- case 38:
- switch(mantissa) {
- case 20: gc_block.modal.motion = MOTION_MODE_PROBE; break; // G38.2
- // NOTE: If G38.3+ are enabled, change mantissa variable type to uint16_t.
- // case 30: gc_block.modal.motion = MOTION_MODE_PROBE_NO_ERROR; break; // G38.3 Not supported.
- // case 40: // Not supported.
- // case 50: // Not supported.
- default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command]
- }
- mantissa = 0; // Set to zero to indicate valid non-integer G command.
- break;
- case 80: gc_block.modal.motion = MOTION_MODE_NONE; break; // G80
- }
+ case 80:
+ word_bit = MODAL_GROUP_G1;
+ gc_block.modal.motion = int_value;
+ if (int_value == 38){
+ if (!((mantissa == 20) || (mantissa == 30) || (mantissa == 40) || (mantissa == 50))) {
+ FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command]
+ }
+ gc_block.modal.motion += (mantissa/10)+100;
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ }
break;
- case 17: case 18: case 19:
- word_bit = MODAL_GROUP_G2;
- switch(int_value) {
- case 17: gc_block.modal.plane_select = PLANE_SELECT_XY; break;
- case 18: gc_block.modal.plane_select = PLANE_SELECT_ZX; break;
- case 19: gc_block.modal.plane_select = PLANE_SELECT_YZ; break;
+ case 17: case 18: case 19:
+ word_bit = MODAL_GROUP_G2;
+ gc_block.modal.plane_select = int_value - 17;
+ break;
+ case 90: case 91:
+ if (mantissa == 0) {
+ word_bit = MODAL_GROUP_G3;
+ gc_block.modal.distance = int_value - 90;
+ } else {
+ word_bit = MODAL_GROUP_G4;
+ if ((mantissa != 10) || (int_value == 90)) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G90.1 not supported]
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ // Otherwise, arc IJK incremental mode is default. G91.1 does nothing.
}
break;
- case 90: case 91:
- word_bit = MODAL_GROUP_G3;
- if (int_value == 90) { gc_block.modal.distance = DISTANCE_MODE_ABSOLUTE; } // G90
- else { gc_block.modal.distance = DISTANCE_MODE_INCREMENTAL; } // G91
+ case 93: case 94:
+ word_bit = MODAL_GROUP_G5;
+ gc_block.modal.feed_rate = 94 - int_value;
break;
- case 93: case 94:
- word_bit = MODAL_GROUP_G5;
- if (int_value == 93) { gc_block.modal.feed_rate = FEED_RATE_MODE_INVERSE_TIME; } // G93
- else { gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; } // G94
+ case 20: case 21:
+ word_bit = MODAL_GROUP_G6;
+ gc_block.modal.units = 21 - int_value;
break;
- case 20: case 21:
- word_bit = MODAL_GROUP_G6;
- if (int_value == 20) { gc_block.modal.units = UNITS_MODE_INCHES; } // G20
- else { gc_block.modal.units = UNITS_MODE_MM; } // G21
+ case 40:
+ word_bit = MODAL_GROUP_G7;
+ // NOTE: Not required since cutter radius compensation is always disabled. Only here
+ // to support G40 commands that often appear in g-code program headers to setup defaults.
+ // gc_block.modal.cutter_comp = CUTTER_COMP_DISABLE; // G40
break;
case 43: case 49:
word_bit = MODAL_GROUP_G8;
// NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed,
// there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49
- // all are explicit axis commands, regardless if they require axis words or not.
+ // all are explicit axis commands, regardless if they require axis words or not.
if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] }
axis_command = AXIS_COMMAND_TOOL_LENGTH_OFFSET;
if (int_value == 49) { // G49
- gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL;
+ gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL;
} else if (mantissa == 10) { // G43.1
gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC;
} else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported G43.x command]
mantissa = 0; // Set to zero to indicate valid non-integer G command.
break;
- case 54: case 55: case 56: case 57: case 58: case 59:
+ case 54: case 55: case 56: case 57: case 58: case 59:
// NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.)
word_bit = MODAL_GROUP_G12;
- gc_block.modal.coord_select = int_value-54; // Shift to array indexing.
+ gc_block.modal.coord_select = int_value - 54; // Shift to array indexing.
+ break;
+ case 61:
+ word_bit = MODAL_GROUP_G13;
+ if (mantissa != 0) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G61.1 not supported]
+ // gc_block.modal.control = CONTROL_MODE_EXACT_PATH; // G61
break;
default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command]
- }
+ }
if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command]
// Check for more than one command per modal group violations in the current block
// NOTE: Variable 'word_bit' is always assigned, if the command is valid.
if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
command_words |= bit(word_bit);
break;
-
+
case 'M':
-
+
// Determine 'M' command and its modal group
if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands]
switch(int_value) {
- case 0: case 1: case 2: case 30:
- word_bit = MODAL_GROUP_M4;
+ case 0: case 1: case 2: case 30:
+ word_bit = MODAL_GROUP_M4;
switch(int_value) {
case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
case 1: break; // Optional stop not supported. Ignore.
- case 2: case 30: gc_block.modal.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
+ default: gc_block.modal.program_flow = int_value; // Program end and reset
}
break;
- case 3: case 4: case 5:
- word_bit = MODAL_GROUP_M7;
+ case 3: case 4: case 5:
+ word_bit = MODAL_GROUP_M7;
switch(int_value) {
case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break;
case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break;
case 5: gc_block.modal.spindle = SPINDLE_DISABLE; break;
}
- break;
- #ifdef ENABLE_M7
- case 7:
- #endif
- case 8: case 9:
- word_bit = MODAL_GROUP_M8;
- switch(int_value) {
- #ifdef ENABLE_M7
- case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break;
- #endif
- case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break;
- case 9: gc_block.modal.coolant = COOLANT_DISABLE; break;
+ break;
+ #ifdef ENABLE_M7
+ case 7: case 8: case 9: case 10:
+ #else
+ case 8: case 9:
+ #endif
+ word_bit = MODAL_GROUP_M8;
+ switch(int_value) {
+ #ifdef ENABLE_M7
+ case 7: gc_block.modal.coolant |= COOLANT_MIST_ENABLE; break;
+ case 10: gc_block.modal.coolant &= ~COOLANT_MIST_ENABLE; break;
+ #endif
+ case 8: gc_block.modal.coolant |= COOLANT_FLOOD_ENABLE; break;
+ case 9: gc_block.modal.coolant = COOLANT_DISABLE; break; // M9 disables both M7 and M8.
}
break;
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ case 56:
+ word_bit = MODAL_GROUP_M9;
+ gc_block.modal.override = OVERRIDE_PARKING_MOTION;
+ break;
+ #endif
+ #ifdef M114M115
+ case 114:
+ report_current_position();
+ break;
+ case 115:
+ report_firmware();
+ break;
+ #endif
default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command]
- }
-
+ }
+
// Check for more than one command per modal group violations in the current block
// NOTE: Variable 'word_bit' is always assigned, if the command is valid.
if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
command_words |= bit(word_bit);
break;
-
+
// NOTE: All remaining letters assign values.
- default:
-
+ default:
+
/* Non-Command Words: This initial parsing phase only checks for repeats of the remaining
legal g-code words and stores their value. Error-checking is performed later since some
words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */
switch(letter){
// case 'A': // Not supported
// case 'B': // Not supported
+ case 'C': word_bit = WORD_C; gc_block.values.xyz[C_AXIS] = value; axis_words |= (1< MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); }
+ gc_block.values.t = int_value;
+ break;
case 'X': word_bit = WORD_X; gc_block.values.xyz[X_AXIS] = value; axis_words |= (1< MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number]
}
// bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking.
-
+
// Track for unused words at the end of error-checking.
// NOTE: Single-meaning value words are removed all at once at the end of error-checking, because
// they are always used when present. This was done to save a few bytes of flash. For clarity, the
// single-meaning value words may be removed as they are used. Also, axis words are treated in the
- // same way. If there is an explicit/implicit axis command, XYZ words are always used and are
- // are removed at the end of error-checking.
-
+ // same way. If there is an explicit/implicit axis command, XYZ words are always used and are
+ // are removed at the end of error-checking.
+
// [1. Comments ]: MSG's NOT SUPPORTED. Comment handling performed by protocol.
-
+
// [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate
// is not defined after switching to G94 from G93.
- if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93
- // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here.
- if (axis_command == AXIS_COMMAND_MOTION_MODE) {
- if ((gc_block.modal.motion != MOTION_MODE_NONE) || (gc_block.modal.motion != MOTION_MODE_SEEK)) {
- if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing]
+ // NOTE: For jogging, ignore prior feed rate mode. Enforce G94 and check for required F word.
+ if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
+ if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); }
+ if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
+ } else {
+ if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93
+ // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here.
+ if (axis_command == AXIS_COMMAND_MOTION_MODE) {
+ if ((gc_block.modal.motion != MOTION_MODE_NONE) && (gc_block.modal.motion != MOTION_MODE_SEEK)) {
+ if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing]
+ }
}
+ // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would
+ // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each
+ // inverse time block, since the commands that use this value already perform undefined checks. This would
+ // also allow other commands, following this switch, to execute and not error out needlessly. This code is
+ // combined with the above feed rate mode and the below set feed rate error-checking.
+
+ // [3. Set feed rate ]: F is negative (done.)
+ // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion.
+ // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word
+ // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error
+ // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires
+ // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined.
+ } else { // = G94
+ // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value.
+ if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94
+ if (bit_istrue(value_words,bit(WORD_F))) {
+ if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
+ } else {
+ gc_block.values.f = gc_state.feed_rate; // Push last state feed rate
+ }
+ } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value.
}
- // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would
- // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each
- // inverse time block, since the commands that use this value already perform undefined checks. This would
- // also allow other commands, following this switch, to execute and not error out needlessly. This code is
- // combined with the above feed rate mode and the below set feed rate error-checking.
-
- // [3. Set feed rate ]: F is negative (done.)
- // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion.
- // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word
- // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error
- // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires
- // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined.
- } else { // = G94
- // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value.
- if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94
- if (bit_istrue(value_words,bit(WORD_F))) {
- if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
- } else {
- gc_block.values.f = gc_state.feed_rate; // Push last state feed rate
- }
- } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value.
- }
+ }
// bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking.
-
+
// [4. Set spindle speed ]: S is negative (done.)
if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; }
// bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking.
-
+
// [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value.
// bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking.
// [6. Change tool ]: N/A
// [7. Spindle control ]: N/A
// [8. Coolant control ]: N/A
- // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED.
-
+ // [9. Override control ]: Not supported except for a Grbl-only parking motion override control.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if (bit_istrue(command_words,bit(MODAL_GROUP_M9))) { // Already set as enabled in parser.
+ if (bit_istrue(value_words,bit(WORD_P))) {
+ if (gc_block.values.p == 0.0) { gc_block.modal.override = OVERRIDE_DISABLED; }
+ bit_false(value_words,bit(WORD_P));
+ }
+ }
+ #endif
+
// [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below.
if (gc_block.non_modal_command == NON_MODAL_DWELL) {
if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing]
bit_false(value_words,bit(WORD_P));
}
-
+
// [11. Set active plane ]: N/A
switch (gc_block.modal.plane_select) {
case PLANE_SELECT_XY:
@@ -478,8 +476,8 @@ uint8_t gc_execute_line(char *line)
axis_0 = Y_AXIS;
axis_1 = Z_AXIS;
axis_linear = X_AXIS;
- }
-
+ }
+
// [12. Set length units ]: N/A
// Pre-convert XYZ coordinate values to millimeters, if applicable.
uint8_t idx;
@@ -490,12 +488,15 @@ uint8_t gc_execute_line(char *line)
}
}
}
-
- // [13. Cutter radius compensation ]: NOT SUPPORTED. Error, if G53 is active.
-
- // [14. Cutter length compensation ]: G43 NOT SUPPORTED, but G43.1 and G49 are.
- // [G43.1 Errors]: Motion command in same line.
- // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid
+
+ // [13. Cutter radius compensation ]: G41/42 NOT SUPPORTED. Error, if enabled while G53 is active.
+ // [G40 Errors]: G2/3 arc is programmed after a G40. The linear move after disabling is less than tool diameter.
+ // NOTE: Since cutter radius compensation is never enabled, these G40 errors don't apply. Grbl supports G40
+ // only for the purpose to not error when G40 is sent with a g-code program header to setup the default modes.
+
+ // [14. Cutter length compensation ]: G43 NOT SUPPORTED, but G43.1 and G49 are.
+ // [G43.1 Errors]: Motion command in same line.
+ // NOTE: Although not explicitly stated so, G43.1 should be applied to only one valid
// axis that is configured (in config.h). There should be an error if the configured axis
// is absent or if any of the other axis words are present.
if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // Indicates called in block.
@@ -503,31 +504,32 @@ uint8_t gc_execute_line(char *line)
if (axis_words ^ (1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
if (gc_state.modal.coord_select != gc_block.modal.coord_select) {
- if (!(settings_read_coord_data(gc_block.modal.coord_select,coordinate_data))) { FAIL(STATUS_SETTING_READ_FAIL); }
+ if (!(settings_read_coord_data(gc_block.modal.coord_select,block_coord_system))) { FAIL(STATUS_SETTING_READ_FAIL); }
}
}
-
- // [16. Set path control mode ]: NOT SUPPORTED.
- // [17. Set distance mode ]: N/A. G90.1 and G91.1 NOT SUPPORTED.
+
+ // [16. Set path control mode ]: N/A. Only G61. G61.1 and G64 NOT SUPPORTED.
+ // [17. Set distance mode ]: N/A. Only G91.1. G90.1 NOT SUPPORTED.
// [18. Set retract mode ]: NOT SUPPORTED.
-
+
// [19. Remaining non-modal actions ]: Check go to predefined position, set G10, or set axis offsets.
// NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these
// commands all treat axis words differently. G10 as absolute offsets or computes current position as
// the axis value, G92 similarly to G10 L20, and G28/30 as an intermediate target position that observes
- // all the current coordinate system and G92 offsets.
+ // all the current coordinate system and G92 offsets.
switch (gc_block.non_modal_command) {
- case NON_MODAL_SET_COORDINATE_DATA:
+ case NON_MODAL_SET_COORDINATE_DATA:
// [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.)
// [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing.
// [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing.
@@ -541,43 +543,47 @@ uint8_t gc_execute_line(char *line)
} else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L]
}
bit_false(value_words,(bit(WORD_L)|bit(WORD_P)));
-
+
// Determine coordinate system to change and try to load from EEPROM.
if (coord_select > 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
else { coord_select = gc_block.modal.coord_select; } // Index P0 as the active coordinate system
- if (!settings_read_coord_data(coord_select,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
-
- // Pre-calculate the coordinate data changes. NOTE: Uses parameter_data since coordinate_data may be in use by G54-59.
+
+ // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
+ if (!settings_read_coord_data(coord_select,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
+
+ // Pre-calculate the coordinate data changes.
for (idx=0; idx WCS = MPos - G92 - TLO - WPos
+ gc_block.values.ijk[idx] = gc_state.position[idx]-gc_state.coord_offset[idx]-gc_block.values.xyz[idx];
+ if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.ijk[idx] -= gc_state.tool_length_offset; }
} else {
// L2: Update coordinate system axis to programmed value.
- parameter_data[idx] = gc_block.values.xyz[idx];
+ gc_block.values.ijk[idx] = gc_block.values.xyz[idx];
}
- }
+ } // Else, keep current stored value.
}
break;
case NON_MODAL_SET_COORDINATE_OFFSET:
// [G92 Errors]: No axis words.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
-
+
// Update axes defined only in block. Offsets current system to defined value. Does not update when
- // active coordinate system is selected, but is still active unless G92.1 disables it.
+ // active coordinate system is selected, but is still active unless G92.1 disables it.
for (idx=0; idx G92 = MPos - WCS - TLO - WPos
+ gc_block.values.xyz[idx] = gc_state.position[idx]-block_coord_system[idx]-gc_block.values.xyz[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.xyz[idx] -= gc_state.tool_length_offset; }
} else {
gc_block.values.xyz[idx] = gc_state.coord_offset[idx];
}
}
break;
-
+
default:
// At this point, the rest of the explicit axis commands treat the axis values as the traditional
@@ -595,7 +601,7 @@ uint8_t gc_execute_line(char *line)
if (gc_block.non_modal_command != NON_MODAL_ABSOLUTE_OVERRIDE) {
// Apply coordinate offsets based on distance mode.
if (gc_block.modal.distance == DISTANCE_MODE_ABSOLUTE) {
- gc_block.values.xyz[idx] += coordinate_data[idx] + gc_state.coord_offset[idx];
+ gc_block.values.xyz[idx] += block_coord_system[idx] + gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.xyz[idx] += gc_state.tool_length_offset; }
} else { // Incremental mode
gc_block.values.xyz[idx] += gc_state.position[idx];
@@ -605,26 +611,34 @@ uint8_t gc_execute_line(char *line)
}
}
}
-
+
// Check remaining non-modal commands for errors.
- switch (gc_block.non_modal_command) {
- case NON_MODAL_GO_HOME_0:
- // [G28 Errors]: Cutter compensation is enabled.
- // Retreive G28 go-home position data (in machine coordinates) from EEPROM
- if (!axis_words) { axis_command = AXIS_COMMAND_NONE; } // Set to none if no intermediate motion.
- if (!settings_read_coord_data(SETTING_INDEX_G28,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); }
- break;
- case NON_MODAL_GO_HOME_1:
- // [G30 Errors]: Cutter compensation is enabled.
- // Retreive G30 go-home position data (in machine coordinates) from EEPROM
- if (!axis_words) { axis_command = AXIS_COMMAND_NONE; } // Set to none if no intermediate motion.
- if (!settings_read_coord_data(SETTING_INDEX_G30,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); }
+ switch (gc_block.non_modal_command) {
+ case NON_MODAL_GO_HOME_0: // G28
+ case NON_MODAL_GO_HOME_1: // G30
+ // [G28/30 Errors]: Cutter compensation is enabled.
+ // Retreive G28/30 go-home position data (in machine coordinates) from EEPROM
+ // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
+ if (gc_block.non_modal_command == NON_MODAL_GO_HOME_0) {
+ if (!settings_read_coord_data(SETTING_INDEX_G28,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); }
+ } else { // == NON_MODAL_GO_HOME_1
+ if (!settings_read_coord_data(SETTING_INDEX_G30,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); }
+ }
+ if (axis_words) {
+ // Move only the axes specified in secondary move.
+ for (idx=0; idx C -----------------+--------------- T <- [x,y]
| <------ d/2 ---->|
-
+
C - Current position
T - Target position
O - center of circle that pass through both C and T
d - distance from C to T
r - designated radius
h - distance from center of CT to O
-
+
Expanding the equations:
-
+
d -> sqrt(x^2 + y^2)
h -> sqrt(4 * r^2 - x^2 - y^2)/2
- i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
+ i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
-
+
Which can be written:
-
+
i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
-
+
Which we for size and speed reasons optimize to:
-
+
h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
i = (x - (y * h_x2_div_d))/2
- j = (y + (x * h_x2_div_d))/2
- */
+ j = (y + (x * h_x2_div_d))/2
+ */
// First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller
// than d. If so, the sqrt of a negative number is complex and error out.
float h_x2_div_d = 4.0 * gc_block.values.r*gc_block.values.r - x*x - y*y;
if (h_x2_div_d < 0) { FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); } // [Arc radius error]
-
+
// Finish computing h_x2_div_d.
h_x2_div_d = -sqrt(h_x2_div_d)/hypot_f(x,y); // == -(h * 2 / d)
// Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
- if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
+ if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
/* The counter clockwise circle lies to the left of the target direction. When offset is positive,
the left hand circle will be generated - when it is negative the right hand circle is generated.
-
+
T <-- Target position
-
- ^
+
+ ^
Clockwise circles with this center | Clockwise circles with this center will have
will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
- \ | /
+ \ | /
center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
|
|
-
- C <-- Current position
- */
- // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
- // even though it is advised against ever generating such circles in a single line of g-code. By
+
+ C <-- Current position
+ */
+ // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
+ // even though it is advised against ever generating such circles in a single line of g-code. By
// inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
// travel and thus we get the unadvisably long arcs as prescribed.
- if (gc_block.values.r < 0) {
- h_x2_div_d = -h_x2_div_d;
+ if (gc_block.values.r < 0) {
+ h_x2_div_d = -h_x2_div_d;
gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc
- }
+ }
// Complete the operation by calculating the actual center of the arc
gc_block.values.ijk[axis_0] = 0.5*(x-(y*h_x2_div_d));
gc_block.values.ijk[axis_1] = 0.5*(y+(x*h_x2_div_d));
-
- } else { // Arc Center Format Offset Mode
+
+ } else { // Arc Center Format Offset Mode
if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); } // [No offsets in plane]
- bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K)));
-
+ bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K)));
+
// Convert IJK values to proper units.
if (gc_block.modal.units == UNITS_MODE_INCHES) {
for (idx=0; idx 0.005) {
+ if (delta_r > 0.005) {
if (delta_r > 0.5) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.5mm
if (delta_r > (0.001*gc_block.values.r)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.005mm AND 0.1% radius
}
}
break;
- case MOTION_MODE_PROBE:
+ case MOTION_MODE_PROBE_TOWARD_NO_ERROR: case MOTION_MODE_PROBE_AWAY_NO_ERROR:
+ gc_parser_flags |= GC_PARSER_PROBE_IS_NO_ERROR; // No break intentional.
+ case MOTION_MODE_PROBE_TOWARD: case MOTION_MODE_PROBE_AWAY:
+ if ((gc_block.modal.motion == MOTION_MODE_PROBE_AWAY) ||
+ (gc_block.modal.motion == MOTION_MODE_PROBE_AWAY_NO_ERROR)) { gc_parser_flags |= GC_PARSER_PROBE_IS_AWAY; }
// [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate
// is undefined. Probe is triggered. NOTE: Probe check moved to probe cycle. Instead of returning
- // an error, it issues an alarm to prevent further motion to the probe. It's also done there to
+ // an error, it issues an alarm to prevent further motion to the probe. It's also done there to
// allow the planner buffer to empty and move off the probe trigger before another probing cycle.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
- if (gc_check_same_position(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target]
+ if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target]
break;
- }
+ }
}
}
-
+
// [21. Program flow ]: No error checks required.
// [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc
- // radius mode, or axis words that aren't used in the block.
- bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words.
+ // radius mode, or axis words that aren't used in the block.
+ if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
+ // Jogging only uses the F feed rate and XYZ value words. N is valid, but S and T are invalid.
+ bit_false(value_words,(bit(WORD_N)|bit(WORD_F)));
+ } else {
+ bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words.
+ }
if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_C))); } // Remove axis words.
if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words]
-
/* -------------------------------------------------------------------------------------
STEP 4: EXECUTE!!
Assumes that all error-checking has been completed and no failure modes exist. We just
need to update the state and execute the block according to the order-of-execution.
- */
+ */
+
+ // Initialize planner data struct for motion blocks.
+ 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
+
+ // Intercept jog commands and complete error checking for valid jog commands and execute.
+ // NOTE: G-code parser state is not updated, except the position to ensure sequential jog
+ // targets are computed correctly. The final parser position after a jog is updated in
+ // protocol_execute_realtime() when jogging completes or is canceled.
+ if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
+ // Only distance and unit modal commands and G53 absolute override command are allowed.
+ // NOTE: Feed rate word and axis word checks have already been performed in STEP 3.
+ if (command_words & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6) | bit(MODAL_GROUP_G0)) ) { FAIL(STATUS_INVALID_JOG_COMMAND) };
+ if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE || gc_block.non_modal_command == NON_MODAL_NO_ACTION)) { FAIL(STATUS_INVALID_JOG_COMMAND); }
+
+ // Initialize planner data to current spindle and coolant modal state.
+ pl_data->spindle_speed = gc_state.spindle_speed;
+ plan_data.condition = (gc_state.modal.spindle | gc_state.modal.coolant);
+
+ uint8_t status = jog_execute(&plan_data, &gc_block);
+ if (status == STATUS_OK) { memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); }
+ return(status);
+ }
+ // If in laser mode, setup laser power based on current and past parser conditions.
+ if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
+ if ( !((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC)
+ || (gc_block.modal.motion == MOTION_MODE_CCW_ARC)) ) {
+ gc_parser_flags |= GC_PARSER_LASER_DISABLE;
+ }
+
+ // Any motion mode with axis words is allowed to be passed from a spindle speed update.
+ // NOTE: G1 and G0 without axis words sets axis_command to none. G28/30 are intentionally omitted.
+ // TODO: Check sync conditions for M3 enabled motions that don't enter the planner. (zero length).
+ if (axis_words && (axis_command == AXIS_COMMAND_MOTION_MODE)) {
+ gc_parser_flags |= GC_PARSER_LASER_ISMOTION;
+ } else {
+ // M3 constant power laser requires planner syncs to update the laser when changing between
+ // a G1/2/3 motion mode state and vice versa when there is no motion in the line.
+ if (gc_state.modal.spindle == SPINDLE_ENABLE_CW) {
+ if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC)
+ || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
+ if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) {
+ gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; // Change from G1/2/3 motion mode.
+ }
+ } else {
+ // When changing to a G1 motion mode without axis words from a non-G1/2/3 motion mode.
+ if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) {
+ gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC;
+ }
+ }
+ }
+ }
+ }
+
+ // [0. Non-specific/common error-checks and miscellaneous setup]:
+ // NOTE: If no line number is present, the value is zero.
+ gc_state.line_number = gc_block.values.n;
+ #ifdef USE_LINE_NUMBERS
+ pl_data->line_number = gc_state.line_number; // Record data for planner use.
+ #endif
+
// [1. Comments feedback ]: NOT SUPPORTED
-
+
// [2. Set feed rate mode ]:
gc_state.modal.feed_rate = gc_block.modal.feed_rate;
-
+ if (gc_state.modal.feed_rate) { pl_data->condition |= PL_COND_FLAG_INVERSE_TIME; } // Set condition flag for planner use.
+
// [3. Set feed rate ]:
gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking.
+ pl_data->feed_rate = gc_state.feed_rate; // Record data for planner use.
// [4. Set spindle speed ]:
- if (gc_state.spindle_speed != gc_block.values.s) {
- gc_state.spindle_speed = gc_block.values.s;
-
- // Update running spindle only if not in check mode and not already enabled.
- if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_state.spindle_speed); }
+ if ((gc_state.spindle_speed != gc_block.values.s) || bit_istrue(gc_parser_flags,GC_PARSER_LASER_FORCE_SYNC)) {
+ if (gc_state.modal.spindle != SPINDLE_DISABLE) {
+ #ifdef VARIABLE_SPINDLE
+ if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_ISMOTION)) {
+ if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) {
+ spindle_sync(gc_state.modal.spindle, 0.0);
+ } else { spindle_sync(gc_state.modal.spindle, gc_block.values.s); }
+ }
+ #else
+ spindle_sync(gc_state.modal.spindle, 0.0);
+ #endif
+ }
+ gc_state.spindle_speed = gc_block.values.s; // Update spindle speed state.
}
-
+ // NOTE: Pass zero spindle speed for all restricted laser motions.
+ if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) {
+ pl_data->spindle_speed = gc_state.spindle_speed; // Record data for planner use.
+ } // else { pl_data->spindle_speed = 0.0; } // Initialized as zero already.
+
// [5. Select tool ]: NOT SUPPORTED. Only tracks tool value.
gc_state.tool = gc_block.values.t;
@@ -851,81 +949,92 @@ uint8_t gc_execute_line(char *line)
// [7. Spindle control ]:
if (gc_state.modal.spindle != gc_block.modal.spindle) {
- gc_state.modal.spindle = gc_block.modal.spindle;
- // Update spindle control and apply spindle speed when enabling it in this block.
- spindle_run(gc_state.modal.spindle, gc_state.spindle_speed);
+ // Update spindle control and apply spindle speed when enabling it in this block.
+ // NOTE: All spindle state changes are synced, even in laser mode. Also, pl_data,
+ // rather than gc_state, is used to manage laser state for non-laser motions.
+ spindle_sync(gc_block.modal.spindle, pl_data->spindle_speed);
+ gc_state.modal.spindle = gc_block.modal.spindle;
}
+ pl_data->condition |= gc_state.modal.spindle; // Set condition flag for planner use.
- // [8. Coolant control ]:
+ // [8. Coolant control ]:
if (gc_state.modal.coolant != gc_block.modal.coolant) {
+ // NOTE: Coolant M-codes are modal. Only one command per line is allowed. But, multiple states
+ // can exist at the same time, while coolant disable clears all states.
+ coolant_sync(gc_block.modal.coolant);
gc_state.modal.coolant = gc_block.modal.coolant;
- coolant_run(gc_state.modal.coolant);
}
-
- // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED
+ pl_data->condition |= gc_state.modal.coolant; // Set condition flag for planner use.
+
+ // [9. Override control ]: NOT SUPPORTED. Always enabled. Except for a Grbl-only parking control.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if (gc_state.modal.override != gc_block.modal.override) {
+ gc_state.modal.override = gc_block.modal.override;
+ mc_override_ctrl_update(gc_state.modal.override);
+ }
+ #endif
// [10. Dwell ]:
if (gc_block.non_modal_command == NON_MODAL_DWELL) { mc_dwell(gc_block.values.p); }
-
+
// [11. Set active plane ]:
- gc_state.modal.plane_select = gc_block.modal.plane_select;
+ gc_state.modal.plane_select = gc_block.modal.plane_select;
// [12. Set length units ]:
gc_state.modal.units = gc_block.modal.units;
- // [13. Cutter radius compensation ]: NOT SUPPORTED
+ // [13. Cutter radius compensation ]: G41/42 NOT SUPPORTED
+ // gc_state.modal.cutter_comp = gc_block.modal.cutter_comp; // NOTE: Not needed since always disabled.
// [14. Cutter length compensation ]: G43.1 and G49 supported. G43 NOT SUPPORTED.
// NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms
// of execution. The error-checking step would simply load the offset value into the correct
- // axis of the block XYZ value array.
+ // axis of the block XYZ value array.
if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // Indicates a change.
gc_state.modal.tool_length = gc_block.modal.tool_length;
- if (gc_state.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) { // G43.1
+ if (gc_state.modal.tool_length == TOOL_LENGTH_OFFSET_CANCEL) { // G49
+ gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS] = 0.0;
+ } // else G43.1
+ if ( gc_state.tool_length_offset != gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS] ) {
gc_state.tool_length_offset = gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS];
- } else { // G49
- gc_state.tool_length_offset = 0.0;
+ system_flag_wco_change();
}
}
-
+
// [15. Coordinate system selection ]:
if (gc_state.modal.coord_select != gc_block.modal.coord_select) {
gc_state.modal.coord_select = gc_block.modal.coord_select;
- memcpy(gc_state.coord_system,coordinate_data,sizeof(coordinate_data));
+ memcpy(gc_state.coord_system,block_coord_system,N_AXIS*sizeof(float));
+ system_flag_wco_change();
}
-
- // [16. Set path control mode ]: NOT SUPPORTED
-
+
+ // [16. Set path control mode ]: G61.1/G64 NOT SUPPORTED
+ // gc_state.modal.control = gc_block.modal.control; // NOTE: Always default.
+
// [17. Set distance mode ]:
gc_state.modal.distance = gc_block.modal.distance;
-
+
// [18. Set retract mode ]: NOT SUPPORTED
-
+
// [19. Go to predefined position, Set G10, or Set axis offsets ]:
switch(gc_block.non_modal_command) {
- case NON_MODAL_SET_COORDINATE_DATA:
- settings_write_coord_data(coord_select,parameter_data);
+ case NON_MODAL_SET_COORDINATE_DATA:
+ settings_write_coord_data(coord_select,gc_block.values.ijk);
// Update system coordinate system if currently active.
- if (gc_state.modal.coord_select == coord_select) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); }
+ if (gc_state.modal.coord_select == coord_select) {
+ memcpy(gc_state.coord_system,gc_block.values.ijk,N_AXIS*sizeof(float));
+ system_flag_wco_change();
+ }
break;
- case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
- // Move to intermediate position before going home. Obeys current coordinate system and offsets
+ case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
+ // Move to intermediate position before going home. Obeys current coordinate system and offsets
// and absolute and incremental modes.
- if (axis_command) {
- #ifdef USE_LINE_NUMBERS
- mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
- #else
- mc_line(gc_block.values.xyz, -1.0, false);
- #endif
- }
- #ifdef USE_LINE_NUMBERS
- mc_line(parameter_data, -1.0, false, gc_block.values.n);
- #else
- mc_line(parameter_data, -1.0, false);
- #endif
- memcpy(gc_state.position, parameter_data, sizeof(parameter_data));
+ pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag.
+ if (axis_command) { mc_line(gc_block.values.xyz, pl_data); }
+ mc_line(gc_block.values.ijk, pl_data);
+ memcpy(gc_state.position, gc_block.values.ijk, N_AXIS*sizeof(float));
break;
- case NON_MODAL_SET_HOME_0:
+ case NON_MODAL_SET_HOME_0:
settings_write_coord_data(SETTING_INDEX_G28,gc_state.position);
break;
case NON_MODAL_SET_HOME_1:
@@ -933,80 +1042,107 @@ uint8_t gc_execute_line(char *line)
break;
case NON_MODAL_SET_COORDINATE_OFFSET:
memcpy(gc_state.coord_offset,gc_block.values.xyz,sizeof(gc_block.values.xyz));
+ system_flag_wco_change();
break;
- case NON_MODAL_RESET_COORDINATE_OFFSET:
+ case NON_MODAL_RESET_COORDINATE_OFFSET:
clear_vector(gc_state.coord_offset); // Disable G92 offsets by zeroing offset vector.
+ system_flag_wco_change();
break;
}
-
+
// [20. Motion modes ]:
- // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes.
+ // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes.
// Enter motion modes only if there are axis words or a motion mode command word in the block.
gc_state.modal.motion = gc_block.modal.motion;
if (gc_state.modal.motion != MOTION_MODE_NONE) {
if (axis_command == AXIS_COMMAND_MOTION_MODE) {
- switch (gc_state.modal.motion) {
- case MOTION_MODE_SEEK:
- #ifdef USE_LINE_NUMBERS
- mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
- #else
- mc_line(gc_block.values.xyz, -1.0, false);
- #endif
- break;
- case MOTION_MODE_LINEAR:
- #ifdef USE_LINE_NUMBERS
- mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
- #else
- mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
- #endif
- break;
- case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
- #ifdef USE_LINE_NUMBERS
- mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
- gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, gc_block.values.n);
- #else
- mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r,
- gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear);
- #endif
- break;
- case MOTION_MODE_PROBE:
- // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So
- // upon a successful probing cycle, the machine position and the returned value should be the same.
- #ifdef USE_LINE_NUMBERS
- mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_block.values.n);
- #else
- mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate);
- #endif
- }
-
+ uint8_t gc_update_pos = GC_UPDATE_POS_TARGET;
+ if (gc_state.modal.motion == MOTION_MODE_LINEAR) {
+ mc_line(gc_block.values.xyz, pl_data);
+ } else if (gc_state.modal.motion == MOTION_MODE_SEEK) {
+ pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag.
+ mc_line(gc_block.values.xyz, pl_data);
+ } else if ((gc_state.modal.motion == MOTION_MODE_CW_ARC) || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
+ mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r,
+ axis_0, axis_1, axis_linear, bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE));
+ } else {
+ // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So
+ // upon a successful probing cycle, the machine position and the returned value should be the same.
+ #ifndef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES
+ pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE;
+ #endif
+ gc_update_pos = mc_probe_cycle(gc_block.values.xyz, pl_data, gc_parser_flags);
+ }
+
// As far as the parser is concerned, the position is now == target. In reality the
// motion control system might still be processing the action and the real tool position
// in any intermediate location.
- memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc.position[] = target[];
- }
+ if (gc_update_pos == GC_UPDATE_POS_TARGET) {
+ memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc_state.position[] = gc_block.values.xyz[]
+ } else if (gc_update_pos == GC_UPDATE_POS_SYSTEM) {
+ gc_sync_position(); // gc_state.position[] = sys_position
+ } // == GC_UPDATE_POS_NONE
+ }
}
-
+
// [21. Program flow ]:
- // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may
+ // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may
// refill and can only be resumed by the cycle start run-time command.
gc_state.modal.program_flow = gc_block.modal.program_flow;
- if (gc_state.modal.program_flow) {
- protocol_buffer_synchronize(); // Finish all remaining buffered motions. Program paused when complete.
- sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued.
-
- // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise,
- // re-enable program flow after pause complete, where cycle start will resume the program.
- if (gc_state.modal.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); }
- else { gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; }
+ if (gc_state.modal.program_flow) {
+ protocol_buffer_synchronize(); // Sync and finish all remaining buffered motions before moving on.
+ if (gc_state.modal.program_flow == PROGRAM_FLOW_PAUSED) {
+ if (sys.state != STATE_CHECK_MODE) {
+ system_set_exec_state_flag(EXEC_FEED_HOLD); // Use feed hold for program pause.
+ protocol_execute_realtime(); // Execute suspend.
+ }
+ } else { // == PROGRAM_FLOW_COMPLETED
+ // Upon program complete, only a subset of g-codes reset to certain defaults, according to
+ // LinuxCNC's program end descriptions and testing. Only modal groups [G-code 1,2,3,5,7,12]
+ // and [M-code 7,8,9] reset to [G1,G17,G90,G94,G40,G54,M5,M9,M48]. The remaining modal groups
+ // [G-code 4,6,8,10,13,14,15] and [M-code 4,5,6] and the modal words [F,S,T,H] do not reset.
+ gc_state.modal.motion = MOTION_MODE_LINEAR;
+ gc_state.modal.plane_select = PLANE_SELECT_XY;
+ gc_state.modal.distance = DISTANCE_MODE_ABSOLUTE;
+ gc_state.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN;
+ // gc_state.modal.cutter_comp = CUTTER_COMP_DISABLE; // Not supported.
+ gc_state.modal.coord_select = 0; // G54
+ gc_state.modal.spindle = SPINDLE_DISABLE;
+ gc_state.modal.coolant = COOLANT_DISABLE;
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ #ifdef DEACTIVATE_PARKING_UPON_INIT
+ gc_state.modal.override = OVERRIDE_DISABLED;
+ #else
+ gc_state.modal.override = OVERRIDE_PARKING_MOTION;
+ #endif
+ #endif
+
+ #ifdef RESTORE_OVERRIDES_AFTER_PROGRAM_END
+ sys.f_override = DEFAULT_FEED_OVERRIDE;
+ sys.r_override = DEFAULT_RAPID_OVERRIDE;
+ sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE;
+ #endif
+
+ // Execute coordinate change and spindle/coolant stop.
+ if (sys.state != STATE_CHECK_MODE) {
+ if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { FAIL(STATUS_SETTING_READ_FAIL); }
+ system_flag_wco_change(); // Set to refresh immediately just in case something altered.
+ spindle_set_state(SPINDLE_DISABLE,0.0);
+ coolant_set_state(COOLANT_DISABLE);
+ }
+ report_feedback_message(MESSAGE_PROGRAM_END);
+ }
+ gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; // Reset program flow.
}
-
- // TODO: % to denote start of program. Sets auto cycle start?
+
+ // TODO: % to denote start of program.
+
return(STATUS_OK);
}
-
-/*
+
+/*
Not supported:
- Canned cycles
@@ -1017,16 +1153,16 @@ uint8_t gc_execute_line(char *line)
- Override control (TBD)
- Tool changes
- Switches
-
+
(*) Indicates optional parameter, enabled through config.h and re-compile
group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets)
group 1 = {G81 - G89} (Motion modes: Canned cycles)
group 4 = {M1} (Optional stop, ignored)
group 6 = {M6} (Tool change)
- group 7 = {G40, G41, G42} cutter radius compensation
- group 8 = {G43} tool length offset (But G43.1/G94 IS SUPPORTED)
- group 8 = {*M7} enable mist coolant
- group 9 = {M48, M49} enable/disable feed and speed override switches
+ group 7 = {G41, G42} cutter radius compensation (G40 is supported)
+ group 8 = {G43} tool length offset (G43.1/G49 are supported)
+ group 8 = {M7*} enable mist coolant (* Compile-option)
+ group 9 = {M48, M49, M56*} enable/disable override switches (* Compile-option)
group 10 = {G98, G99} return mode canned cycles
- group 13 = {G61, G61.1, G64} path control mode
+ group 13 = {G61.1, G64} path control mode (G61 is supported)
*/
diff --git a/gcode.h b/gcode.h
index f28a79c3b..beb8a2286 100644
--- a/gcode.h
+++ b/gcode.h
@@ -1,10 +1,10 @@
/*
gcode.h - rs274/ngc parser.
- Part of Grbl v0.9
+ Part of Grbl
+
+ Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
+ Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012-2014 Sungeun K. Jeon
- Copyright (c) 2014 Bob Beattie
-
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,102 +18,124 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef gcode_h
#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
-#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G80] Motion
+#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion
#define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection
#define MODAL_GROUP_G3 3 // [G90,G91] Distance mode
-#define MODAL_GROUP_G5 4 // [G93,G94] Feed rate mode
-#define MODAL_GROUP_G6 5 // [G20,G21] Units
-#define MODAL_GROUP_G8 6 // [G43,G43.1,G49] Tool length offset
-#define MODAL_GROUP_G12 7 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
-
-#define MODAL_GROUP_M4 8 // [M0,M1,M2,M30] Stopping
-#define MODAL_GROUP_M7 9 // [M3,M4,M5] Spindle turning
-#define MODAL_GROUP_M8 10 // [M7,M8,M9] Coolant control
-
-#define OTHER_INPUT_F 11
-#define OTHER_INPUT_S 12
-#define OTHER_INPUT_T 13
+#define MODAL_GROUP_G4 4 // [G91.1] Arc IJK distance mode
+#define MODAL_GROUP_G5 5 // [G93,G94] Feed rate mode
+#define MODAL_GROUP_G6 6 // [G20,G21] Units
+#define MODAL_GROUP_G7 7 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED.
+#define MODAL_GROUP_G8 8 // [G43.1,G49] Tool length offset
+#define MODAL_GROUP_G12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
+#define MODAL_GROUP_G13 10 // [G61] Control mode
+
+#define MODAL_GROUP_M4 11 // [M0,M1,M2,M30] Stopping
+#define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning
+#define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control
+#define MODAL_GROUP_M9 14 // [M56] Override control
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute.
+// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing
+// compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not
+// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c
+// to see how they are used, if you need to alter them.
// Modal Group G0: Non-modal actions
#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero)
-#define NON_MODAL_DWELL 1 // G4
-#define NON_MODAL_SET_COORDINATE_DATA 2 // G10
-#define NON_MODAL_GO_HOME_0 3 // G28
-#define NON_MODAL_SET_HOME_0 4 // G28.1
-#define NON_MODAL_GO_HOME_1 5 // G30
-#define NON_MODAL_SET_HOME_1 6 // G30.1
-#define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53
-#define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92
-#define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1
+#define NON_MODAL_DWELL 4 // G4 (Do not alter value)
+#define NON_MODAL_SET_COORDINATE_DATA 10 // G10 (Do not alter value)
+#define NON_MODAL_GO_HOME_0 28 // G28 (Do not alter value)
+#define NON_MODAL_SET_HOME_0 38 // G28.1 (Do not alter value)
+#define NON_MODAL_GO_HOME_1 30 // G30 (Do not alter value)
+#define NON_MODAL_SET_HOME_1 40 // G30.1 (Do not alter value)
+#define NON_MODAL_ABSOLUTE_OVERRIDE 53 // G53 (Do not alter value)
+#define NON_MODAL_SET_COORDINATE_OFFSET 92 // G92 (Do not alter value)
+#define NON_MODAL_RESET_COORDINATE_OFFSET 102 //G92.1 (Do not alter value)
// Modal Group G1: Motion modes
#define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero)
-#define MOTION_MODE_LINEAR 1 // G1
-#define MOTION_MODE_CW_ARC 2 // G2
-#define MOTION_MODE_CCW_ARC 3 // G3
-#define MOTION_MODE_PROBE 4 // G38.2
-#define MOTION_MODE_NONE 5 // G80
+#define MOTION_MODE_LINEAR 1 // G1 (Do not alter value)
+#define MOTION_MODE_CW_ARC 2 // G2 (Do not alter value)
+#define MOTION_MODE_CCW_ARC 3 // G3 (Do not alter value)
+#define MOTION_MODE_PROBE_TOWARD 140 // G38.2 (Do not alter value)
+#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 141 // G38.3 (Do not alter value)
+#define MOTION_MODE_PROBE_AWAY 142 // G38.4 (Do not alter value)
+#define MOTION_MODE_PROBE_AWAY_NO_ERROR 143 // G38.5 (Do not alter value)
+#define MOTION_MODE_NONE 80 // G80 (Do not alter value)
// Modal Group G2: Plane select
#define PLANE_SELECT_XY 0 // G17 (Default: Must be zero)
-#define PLANE_SELECT_ZX 1 // G18
-#define PLANE_SELECT_YZ 2 // G19
+#define PLANE_SELECT_ZX 1 // G18 (Do not alter value)
+#define PLANE_SELECT_YZ 2 // G19 (Do not alter value)
// Modal Group G3: Distance mode
#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero)
-#define DISTANCE_MODE_INCREMENTAL 1 // G91
+#define DISTANCE_MODE_INCREMENTAL 1 // G91 (Do not alter value)
+
+// Modal Group G4: Arc IJK distance mode
+#define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero)
// Modal Group M4: Program flow
#define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero)
-#define PROGRAM_FLOW_PAUSED 1 // M0, M1
-#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
+#define PROGRAM_FLOW_PAUSED 3 // M0
+#define PROGRAM_FLOW_OPTIONAL_STOP 1 // M1 NOTE: Not supported, but valid and ignored.
+#define PROGRAM_FLOW_COMPLETED_M2 2 // M2 (Do not alter value)
+#define PROGRAM_FLOW_COMPLETED_M30 30 // M30 (Do not alter value)
// 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 1 // G93 (Do not alter value)
// Modal Group G6: Units mode
#define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
-#define UNITS_MODE_INCHES 1 // G20
+#define UNITS_MODE_INCHES 1 // G20 (Do not alter value)
+
+// Modal Group G7: Cutter radius compensation mode
+#define CUTTER_COMP_DISABLE 0 // G40 (Default: Must be zero)
+
+// Modal Group G13: Control mode
+#define CONTROL_MODE_EXACT_PATH 0 // G61 (Default: Must be zero)
// 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)
#define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1
+// Modal Group M9: Override control
+#ifdef DEACTIVATE_PARKING_UPON_INIT
+ #define OVERRIDE_DISABLED 0 // (Default: Must be zero)
+ #define OVERRIDE_PARKING_MOTION 1 // M56
+#else
+ #define OVERRIDE_PARKING_MOTION 0 // M56 (Default: Must be zero)
+ #define OVERRIDE_DISABLED 1 // Parking disabled.
+#endif
+
// Modal Group G12: Active work coordinate system
// N/A: Stores coordinate system value (54-59) to change to.
+// Define parameter word mapping.
#define WORD_F 0
#define WORD_I 1
#define WORD_J 2
@@ -127,28 +149,57 @@
#define WORD_X 10
#define WORD_Y 11
#define WORD_Z 12
-#define WORD_C 13 // additional bit definition
-
+#define WORD_C 13
+
+// Define g-code parser position updating flags
+#define GC_UPDATE_POS_TARGET 0 // Must be zero
+#define GC_UPDATE_POS_SYSTEM 1
+#define GC_UPDATE_POS_NONE 2
+
+// Define probe cycle exit states and assign proper position updating.
+#define GC_PROBE_FOUND GC_UPDATE_POS_SYSTEM
+#define GC_PROBE_ABORT GC_UPDATE_POS_NONE
+#define GC_PROBE_FAIL_INIT GC_UPDATE_POS_NONE
+#define GC_PROBE_FAIL_END GC_UPDATE_POS_TARGET
+#ifdef SET_CHECK_MODE_PROBE_TO_START
+ #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE
+#else
+ #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_TARGET
+#endif
+// Define gcode parser flags for handling special cases.
+#define GC_PARSER_NONE 0 // Must be zero.
+#define GC_PARSER_JOG_MOTION bit(0)
+#define GC_PARSER_CHECK_MANTISSA bit(1)
+#define GC_PARSER_ARC_IS_CLOCKWISE bit(2)
+#define GC_PARSER_PROBE_IS_AWAY bit(3)
+#define GC_PARSER_PROBE_IS_NO_ERROR bit(4)
+#define GC_PARSER_LASER_FORCE_SYNC bit(5)
+#define GC_PARSER_LASER_DISABLE bit(6)
+#define GC_PARSER_LASER_ISMOTION bit(7)
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
- uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
- uint8_t feed_rate; // {G93,G94}
- uint8_t units; // {G20,G21}
- uint8_t distance; // {G90,G91}
- uint8_t plane_select; // {G17,G18,G19}
- uint8_t tool_length; // {G43.1,G49}
- uint8_t coord_select; // {G54,G55,G56,G57,G58,G59}
- uint8_t program_flow; // {M0,M1,M2,M30}
- uint8_t coolant; // {M7,M8,M9}
- uint8_t spindle; // {M3,M4,M5}
-} gc_modal_t;
+ uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
+ uint8_t feed_rate; // {G93,G94}
+ uint8_t units; // {G20,G21}
+ uint8_t distance; // {G90,G91}
+ // uint8_t distance_arc; // {G91.1} NOTE: Don't track. Only default supported.
+ uint8_t plane_select; // {G17,G18,G19}
+ // uint8_t cutter_comp; // {G40} NOTE: Don't track. Only default supported.
+ uint8_t tool_length; // {G43.1,G49}
+ uint8_t coord_select; // {G54,G55,G56,G57,G58,G59}
+ // uint8_t control; // {G61} NOTE: Don't track. Only default supported.
+ uint8_t program_flow; // {M0,M1,M2,M30}
+ uint8_t coolant; // {M7,M8,M9}
+ uint8_t spindle; // {M3,M4,M5}
+ uint8_t override; // {M56}
+} gc_modal_t;
typedef struct {
float f; // Feed
- float ijk[4]; // I,J,K Axis arc offsets. Now 4 array elements.
+ float ijk[4]; // I,J,K Axis arc offsets
uint8_t l; // G10 or canned cycles parameters
int32_t n; // Line number
float p; // G10 or dwell parameters
@@ -156,37 +207,35 @@ typedef struct {
float r; // Arc radius
float s; // Spindle speed
uint8_t t; // Tool selection
- float xyz[4]; // float xyz[4]; // X,Y,Z,C translational axes. Now 4 array elements.
+ float xyz[4]; // X,Y,Z Translational axes
} gc_values_t;
typedef struct {
gc_modal_t modal;
-
+
float spindle_speed; // RPM
float feed_rate; // Millimeters/min
uint8_t tool; // Tracks tool number. NOT USED.
+ int32_t line_number; // Last line number sent
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();
@@ -195,6 +244,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
diff --git a/grbl.h b/grbl.h
new file mode 100644
index 000000000..b32a95ec5
--- /dev/null
+++ b/grbl.h
@@ -0,0 +1,140 @@
+/*
+ grbl.h - main Grbl include file
+ Part of Grbl
+
+ 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+*/
+
+#ifndef grbl_h
+#define grbl_h
+
+// Grbl versioning system
+#define GRBL_VERSION "1.1h"
+#define GRBL_VERSION_BUILD "20190830"
+
+// Define standard libraries used by Grbl.
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Define the Grbl system include files. NOTE: Do not alter organization.
+#include "config.h"
+#include "nuts_bolts.h"
+#include "settings.h"
+#include "system.h"
+#include "defaults.h"
+#include "cpu_map.h"
+#include "planner.h"
+#include "coolant_control.h"
+#include "eeprom.h"
+#include "gcode.h"
+#include "limits.h"
+#include "motion_control.h"
+#include "planner.h"
+#include "print.h"
+#include "probe.h"
+#include "protocol.h"
+#include "report.h"
+#include "serial.h"
+#include "spindle_control.h"
+#include "stepper.h"
+#include "jog.h"
+
+// ---------------------------------------------------------------------------------------
+// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
+
+#ifndef HOMING_CYCLE_0
+ #error "Required HOMING_CYCLE_0 not defined."
+#endif
+
+#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(VARIABLE_SPINDLE)
+ #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with VARIABLE_SPINDLE enabled"
+#endif
+
+#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(CPU_MAP_ATMEGA328P_4_AXIS)
+ #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with a 328p processor"
+#endif
+
+#if !defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)
+ #error "SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED may only be used with USE_SPINDLE_DIR_AS_ENABLE_PIN enabled"
+#endif
+
+#if defined(PARKING_ENABLE)
+ #if defined(HOMING_FORCE_SET_ORIGIN)
+ #error "HOMING_FORCE_SET_ORIGIN is not supported with PARKING_ENABLE at this time."
+ #endif
+#endif
+
+#if defined(ENABLE_PARKING_OVERRIDE_CONTROL)
+ #if !defined(PARKING_ENABLE)
+ #error "ENABLE_PARKING_OVERRIDE_CONTROL must be enabled with PARKING_ENABLE."
+ #endif
+#endif
+
+#if defined(SPINDLE_PWM_MIN_VALUE)
+ #if !(SPINDLE_PWM_MIN_VALUE > 0)
+ #error "SPINDLE_PWM_MIN_VALUE must be greater than zero."
+ #endif
+#endif
+
+#if (REPORT_WCO_REFRESH_BUSY_COUNT < REPORT_WCO_REFRESH_IDLE_COUNT)
+ #error "WCO busy refresh is less than idle refresh."
+#endif
+#if (REPORT_OVR_REFRESH_BUSY_COUNT < REPORT_OVR_REFRESH_IDLE_COUNT)
+ #error "Override busy refresh is less than idle refresh."
+#endif
+#if (REPORT_WCO_REFRESH_IDLE_COUNT < 2)
+ #error "WCO refresh must be greater than one."
+#endif
+#if (REPORT_OVR_REFRESH_IDLE_COUNT < 1)
+ #error "Override refresh must be greater than zero."
+#endif
+
+#if defined(ENABLE_DUAL_AXIS)
+ #if !((DUAL_AXIS_SELECT == X_AXIS) || (DUAL_AXIS_SELECT == Y_AXIS))
+ #error "Dual axis currently supports X or Y axes only."
+ #endif
+ #if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(VARIABLE_SPINDLE)
+ #error "VARIABLE_SPINDLE not supported with DUAL_AXIS_CNC_SHIELD_CLONE."
+ #endif
+ #if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51)
+ #error "More than one dual axis configuration found. Select one."
+ #endif
+ #if !defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && !defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51)
+ #error "No supported dual axis configuration found. Select one."
+ #endif
+ #if defined(COREXY)
+ #error "CORE XY not supported with dual axis feature."
+ #endif
+ #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
+ #error "USE_SPINDLE_DIR_AS_ENABLE_PIN not supported with dual axis feature."
+ #endif
+ #if defined(ENABLE_M7)
+ #error "ENABLE_M7 not supported with dual axis feature."
+ #endif
+#endif
+
+// ---------------------------------------------------------------------------------------
+
+#endif
diff --git a/grbl.hex b/grbl.hex
new file mode 100644
index 000000000..8a0e8ab43
--- /dev/null
+++ b/grbl.hex
@@ -0,0 +1,1881 @@
+:100000000C940A020C9427020C9427020C94B3243B
+:100010000C94C3210C9427020C9427020C94270201
+:100020000C9427020C9427020C9427020C94E419D8
+:100030000C9427020C9427020C9427020C9427029C
+:100040000C94BC310C9427020C94A7340C94E1312D
+:100050000C9427020C9427020C9427020C9427027C
+:100060000C9427020C942702720A780A7B0A7E0AF3
+:10007000830A8B0A930A2B0B2B0B2B0BA00AA30AC8
+:10008000AC0AB50A2B0B2B0B2B0B2B0B2B0B2B0BB7
+:10009000C10ACB0AD70ADF0AE20AEB0AF40AFA0A13
+:1000A0002B0B2B0B030B0C0B170B980D6C196C19EE
+:1000B000A50D6C196C19AF0DBC0DC90DD60D6C19C1
+:1000C000DA0D6C19E80D6C19F20DFC0D060E6C19A9
+:1000D0006C196C19160E230E300EDD34DF341A3510
+:1000E0001A351A351A351A351A351A351A351A3598
+:1000F0001A35E534E734E934EB34ED34EF34F134D8
+:10010000F3341A35F734F934FB34FD34FF34013558
+:100110001A3503350535084AD73B3BCE016E84BC02
+:10012000BFFDC12F3D6C74319ABD56833DDA3D0051
+:10013000C77F11BED9E4BB4C3E916BAAAABE00009A
+:1001400000803F05A84CCDB2D44EB93836A9020C78
+:1001500050B9918688083CA6AAAA2ABE0000008051
+:100160003F000D0A4772626C20312E3168205B27F8
+:10017000242720666F722068656C705D0D0A00246C
+:100180004E005B4F50543A005B5645523A312E3187
+:10019000682E32303139303833303A005B544C4FAE
+:1001A0003A005B4739323A003330003238005B475F
+:1001B00000204700204D002053002046002054001E
+:1001C00033382E005B47433A47005B484C503A2493
+:1001D0002420242320244720244920244E2024782E
+:1001E0003D76616C20244E783D6C696E6520244A12
+:1001F0003D6C696E652024534C50202443202458C4
+:10020000202448207E2021203F206374726C2D78AA
+:100210005D0D0A00536C656570696E670052657309
+:10022000746F72696E67207370696E646C650052DA
+:100230006573746F72696E672064656661756C744E
+:10024000730050676D20456E6400436865636B20E2
+:100250004C696D69747300436865636B20446F6F0C
+:10026000720044697361626C656400456E61626C22
+:1002700065640043617574696F6E3A20556E6C6FEA
+:10028000636B656400272448277C27245827207443
+:100290006F20756E6C6F636B0052657365742074AC
+:1002A0006F20636F6E74696E7565005B4D53473ADE
+:1002B000004649524D574152455F4E414D453A47E0
+:1002C00072626C2C204649524D574152455F55523F
+:1002D0004C3A68747470733A2F2F6769746875624A
+:1002E0002E636F6D2F6F70656E706E702F67726208
+:1002F0006C2C204649524D574152455F564552534A
+:10030000494F4E3A20312E31680D0A00414C41527E
+:100310004D3A0043726173682050726576656E7461
+:1003200065642120506C732052656F6E6E65637436
+:1003300020616E642052652D686F6D65007C413AC6
+:10034000007C4F763A007C57434F3A007C506E3A1F
+:10035000007C46533A007C42663A007C57506F73EB
+:100360003A007C4D506F733A00536C6565700044E1
+:100370006F6F723A00436865636B00416C61726D28
+:1003800000486F6D65004A6F6700486F6C643A0003
+:1003900052756E0049646C65009A9931429A9931A0
+:1003A000426646844304560E4100401C4600401CF1
+:1003B000460000C84400401C4700BADB4900BADBD5
+:1003C0004900BA5B4A00BADB4A00009BC30000A5A3
+:1003D000C30000F0C1000000800A000419010AD720
+:1003E000233C6F12033B00007A4400000000000031
+:1003F000000018430000A041FA00000000000D0AB0
+:10040000006F6B0D0A005B5052423A006572726FCA
+:10041000723A000011241FBECFEFD8E0DEBFCDBF7F
+:1004200011E0A0E0B1E0E2E6F5E702C005900D9230
+:10043000A830B107D9F727E0A8E0B1E001C01D92CC
+:10044000A93FB207E1F70E9477050C94AF3A0C94EC
+:1004500000002F923F924F925F926F927F928F9205
+:100460009F92AF92BF92CF92DF92EF92FF920F9343
+:100470001F93CF93DF93CDB7DEB7C455D1090FB625
+:10048000F894DEBF0FBECDBF182F092F7B018091DE
+:10049000900185FF26C0D0902A0120E2D21631F1CA
+:1004A000812F0E9435218823E1F081E080932D0186
+:1004B00038E0D31641F00E94853482E00E941C216E
+:1004C0000E9412330EC088E00E942C210E94123339
+:1004D00080912B01811105C080912A018111F6CFF5
+:1004E000EACF80912A01823009F4E7C20E941233D8
+:1004F00080912B018111E1C290919205809131028E
+:10050000981303C00E94D931F1CF80919305282E12
+:10051000312C96E3929D9001939D300D1124A901F9
+:100520004E5C5D4F2B965FAF4EAF2B97FA01119249
+:100530009A95E9F7F7019085FA01958BF7014481C7
+:100540005581668177812B96EEADFFAD2B9742AB3F
+:1005500053AB64AB75AB91FF4CC090E1EAE1F1E0C5
+:10056000DE01919601900D929A95E1F724961FAFC6
+:10057000249725960FAF25974BE351E027965FAF61
+:100580004EAF2797FE01F1966396FFAFEEAF6397EC
+:100590009E012F5D3F4F61963FAF2EAF619736E3CF
+:1005A000839FC0011124AC014E5C5D4F2F965FAF5D
+:1005B0004EAF2F97CE0141962D969FAF8EAF2D97C0
+:1005C00029969FAF8EAF299710E086E3829D900118
+:1005D000839D300D1124C9018E5C9D4FFC017096E6
+:1005E0006596FFAFEEAF65976C01F4E1CF0ED11CBD
+:1005F00044C090E1E4E9F5E0B3CF2F96EEADFFAD56
+:100600002F9721933193419351932F96FFAFEEAFE4
+:100610002F976596EEADFFAD659780809180A280A3
+:10062000B38082169306A406B50610F449015A0158
+:100630006596EEADFFAD659780829182A282B3820E
+:100640000E948937A30192010E94DF362996EEAD00
+:10065000FFAD299761937193819391932996FFAF91
+:10066000EEAF299720E030E0A9010E94D53687FD42
+:100670004AC01F5F143009F458C02596EEADFFAD97
+:10068000259761917191819191912596FFAFEEAF80
+:1006900025972796EEADFFAD279741905190619039
+:1006A00071902796FFAFEEAF2797A30192010E94AA
+:1006B00037390E9404396396EEADFFAD63976193BD
+:1006C0007193819391936396FFAFEEAF63976196B9
+:1006D000EEADFFAD619781909190A190B190619640
+:1006E000FFAFEEAF6197681979098A099B099B01F1
+:1006F000AC0197FF82CF22273327A901261B370B96
+:10070000480B590B7ACF112331F0113031F01230F0
+:1007100031F080E105C081E003C082E001C084E0E7
+:10072000F6019081892B8083A4CF89288A288B2881
+:1007300009F4B4C1CE0141960E9473254B015C01BE
+:1007400086E3829D8001839D100D11240E5C1D4F58
+:10075000F80182A293A2A4A2B5A2BE016F5E7F4F50
+:100760008BE591E00E941D25F801668F778F80A3AD
+:1007700091A3BE016F5E7F4F8BE491E00E941D2527
+:10078000F80162A773A784A795A7D588D0FC12C0EB
+:10079000F7012081318142815381D3FC06C0F801E9
+:1007A00026A737A740AB51AB09C0C501B4010E94D1
+:1007B0003739F80166A777A780AB91AB9091930585
+:1007C00080919205981711F0D1FE0DC086E3829DAD
+:1007D000F001839DF00D1124EE5CFD4F168A178AFF
+:1007E000108E118E68C024EA35E025963FAF2EAFFB
+:1007F00025978E010F5F1F4FAE014F5D5F4F239610
+:100800005FAF4EAF2397C12CD12C760126960FAF48
+:10081000269728961FAF28972596EEADFFAD259712
+:1008200081909190A190B1902596FFAFEEAF259762
+:100830002D96EEADFFAD2D97419051906190719046
+:100840002D96FFAFEEAF2D97A3019201C501B40124
+:100850000E9437399B01AC01C701B6010E94DF3508
+:100860006B017C01A5019401C301B2010E94DF3537
+:10087000F80161937193819391938F0123962EAD2B
+:100880003FAD23972D964EAD5FAD2D9724173507BD
+:1008900019F62FEE3FEF4FE75FE3C701B6010E9465
+:1008A000F73818166CF486E3829DF001839DF00DF5
+:1008B0001124EE5CFD4F16A217A210A611A679C056
+:1008C0002FEE3FEF4FE75FEBC701B6010E94D53631
+:1008D00087FF11C086E3829DF001839DF00D1124F6
+:1008E000EE5CFD4F89E996E7A6E9BEE786A397A3EC
+:1008F000A0A7B1A75EC026968FAD269728969FAD7C
+:1009000028970E94732526966FAD269728967FAD6F
+:1009100028978BE591E00E941D254B015C01A70102
+:10092000960160E070E080E89FE30E94DF3520E000
+:1009300030E040E05FE30E9437390E94D6396B0116
+:100940007C01209180013091810140918201509180
+:100950008301C501B4010E943739A70196010E94A5
+:1009600037394B015C01A701960160E070E080E837
+:100970009FE30E94DF359B01AC01C501B4010E94D9
+:10098000DF366B017C0120E030E0A9010E94D53602
+:1009900087FF03C0C12CD12C760186E3829DF00134
+:1009A000839DF00D1124EE5CFD4FC6A2D7A2E0A6F8
+:1009B000F1A686E3829D8001839D100D11240E5CBB
+:1009C0001D4FF801858981FD78C02B968EAD9FADB6
+:1009D0002B970E941A266B017C018090B4059090A1
+:1009E000B505A090B605B090B705A50194010E9489
+:1009F000F73818162CF4A5019401C501B40104C000
+:100A0000A7019601C701B6010E943739F801628F2C
+:100A1000738F848F958F86E3829D8001839D100D57
+:100A200011240E5C1D4FF80186A097A0A0A4B1A4CC
+:100A3000A5019401628D738D848D958D0E94F73888
+:100A400018162CF4F801828E938EA48EB58EC09267
+:100A5000B405D092B505E092B605F092B70580E1F5
+:100A6000FE017196A4EAB5E001900D928A95E1F736
+:100A700080E1FE01F196A4E9B5E001900D928A951E
+:100A8000E1F780913102809393058F5F803109F403
+:100A900080E0809331020E94AE260FC080919001C9
+:100AA00081FF0BC0F701808584FF07C04481558119
+:100AB0006681778180E10E943534CC5ADF4F0FB6D2
+:100AC000F894DEBF0FBECDBFDF91CF911F910F9184
+:100AD000FF90EF90DF90CF90BF90AF909F908F905E
+:100AE0007F906F905F904F903F902F900895CF939D
+:100AF000DF93CDB7DEB765970FB6F894DEBF0FBEB4
+:100B0000CDBF8091C00082608093C0001092C5006C
+:100B100080E18093C4008091C10088698093C10006
+:100B200080E090E00E9468298A3009F0CFC545E650
+:100B300050E061E070E08BE391E00E94F627892BA2
+:100B400009F4C4C58AB18C678AB9579A84B18761A0
+:100B500084B9809181008F7E809381008091810093
+:100B6000886080938100809180008C7F80938000DA
+:100B7000809180008F708093800080916E00887FCC
+:100B800080936E0014BC15BC80916E0081608093D0
+:100B90006E0087B1887F87B988B1876088B98091F6
+:100BA0006C00876080936C00809168008260809305
+:100BB000680080E1EAE1F1E0DF011D928A95E9F742
+:100BC000789410922A018091900184FF03C081E003
+:100BD00080932A01FAE22F2EF1E03F2E992493947C
+:100BE00080912A0191E1F10111929A95E9F78093A0
+:100BF0002A0184E68093310180933201809333018E
+:100C000080E1A9E0B1E01D928A95E9F710921901FF
+:100C10001092B8051092B9051092BA051092BB0552
+:100C200080912E0280932F028CE4E0EAF1E0119291
+:100C30008A95E9F768EC71E080E00E94C82881118C
+:100C400003C087E00E942A230E9453343C9A3D9AB5
+:100C50000E9438320E9403253E98469A80E00E9406
+:100C6000FD210E949D260E9487310E9409260E9434
+:100C70007F3482E691E00E9417238091900183FFE8
+:100C800010C00E94E224882361F090922A018BEA2E
+:100C900092E00E9417238AE492E00E9417230E94A8
+:100CA000842480912A01817899F08BEA92E00E9455
+:100CB000172385E892E00E9417230E9484249092D3
+:100CC0002A0110E000E072E0E72EE3E08E2E50C033
+:100CD00010922A0110E0812F0E94772881110EC006
+:100CE0001092A9078EE30E940B320E94FD208AE336
+:100CF0000E940B3287E00E942A2312C08091A9072C
+:100D0000882371F00E946F0B082F8EE30E940B3234
+:100D10000E94FD208AE30E940B32802F0E9436231E
+:100D20001F5F1230C1F6CDCFE92FF0E0E754F94F45
+:100D300080819F5F913809F490E090932F028F3F5C
+:100D4000E9F08A3021F50E94123380912B01811144
+:100D500047CFE12FF0E0E755F84F1082F02FF17008
+:100D6000FF2E00FF18C08BE00E942A2310E000E055
+:100D700090912F0280912E029813D6CF0E94D931E4
+:100D80000E94123380912B01882391F329CF8D305B
+:100D900009F076C4D8CF8091A907882309F46DC4DF
+:100DA000843209F062C49D8A8091AA07833409F1D4
+:100DB00034F4882361F0843209F017C11AC08A34F0
+:100DC00059F08835B1F0873409F00FC112C08AECB0
+:100DD00091E00E94172347C480912A018F7D09F07A
+:100DE00032C48091AB078D3309F035C40E946F0B7C
+:100DF0002DC49091AB0791112EC4833409F4C3C064
+:100E00001CF4843239F02FC4873469F0883509F432
+:100E1000DAC029C480912A018871F82E09F013C420
+:100E20000E94722320C484EC91E00E9417238091D9
+:100E3000A0018C3838F080EC91E00E94172380915B
+:100E4000A0018A580E94F12281EB91E00E941723B1
+:100E50008091A6018A5C0E94F12281EB91E00E94C0
+:100E600017238091A4018F5E0E94F12281EB91E013
+:100E70000E9417238091A20125E1281B822F0E9446
+:100E8000F12281EB91E00E9417238091A301865A01
+:100E90000E94F12281EB91E00E9417238091A10131
+:100EA0009EE5981B892F0E94F1228091A70188233B
+:100EB00099F084EB91E00E9417238091A701833081
+:100EC00029F08E3139F0823039F404C080E30E9479
+:100ED0000B3202C00E94F12284EB91E00E941723A2
+:100EE0008091A901803131F0803231F0811107C049
+:100EF00085E303C083E301C084E30E940B32809149
+:100F0000A801882399F087FF07C084EB91E00E9435
+:100F1000172387E30E940B328091A80186FF0DC042
+:100F200084EB91E00E94172388E305C084EB91E0F5
+:100F30000E94172389E30E940B328DEB91E00E94FF
+:100F400017238091B3010E94F1228AEB91E00E9465
+:100F500017236091AF017091B0018091B101909120
+:100F6000B2010E94AC2287EB91E00E9417236091AE
+:100F7000AB017091AC018091AD019091AE0140E068
+:100F80000E940F220EC080912A01823069F40E94D3
+:100F900085348BEA92E00E94172382E692E00E9459
+:100FA00017230E9484245FC381114DC3E0922A015C
+:100FB0008BEA92E00E9417238BE692E00E941723AF
+:100FC0000E9484243BC080912A01813009F04BC3E8
+:100FD0008BEA92E00E94172383E792E00E94172396
+:100FE0000E94842410922A013EC390912A019230DB
+:100FF00008F029C3893409F4FAC034F4833271F05B
+:10100000883409F45AC0A1C1823509F448C1833536
+:1010100009F4DBC08E3409F098C171C18091AB072F
+:10102000811119C310E0BE016F5F7F4F812F0E94B5
+:10103000C828811105C087E00E942A23F12C13C320
+:101040008EEA91E00E941723163029F0173041F400
+:1010500088EA91E002C08BEA91E00E94172304C065
+:1010600086E3810F0E94F1228AE30E940B32CE01B7
+:1010700001960E94CA220E9484241F5F183099F6AC
+:1010800082EA91E00E94172388ED91E00E94CA2233
+:101090000E9484248CE991E00E9417236091E8016A
+:1010A0007091E9018091EA019091EB010E94BB22CD
+:1010B0000E9484240E948924D6C28091AB078111AA
+:1010C000D2C2809190018062809390012091920120
+:1010D00030919301409194015091950160916B0181
+:1010E00070916C0180916D0190916E010E94E035CC
+:1010F00020913B0130913C0140913D0150913E01D6
+:101100000E9437390E94513760931A0170931B0176
+:1011100080931C0190931D012091960130919701BD
+:10112000409198015091990160916F017091700107
+:1011300080917101909172010E94E03520913F01F0
+:101140003091400140914101509142010E94373954
+:101150000E94513760931E0170931F0180932001FC
+:101160009093210120919C0130919D0140919E011D
+:1011700050919F016091730170917401809175018C
+:10118000909176010E94E035209143013091440115
+:1011900040914501509146010E9437390E945137D4
+:1011A0006093220170932301809324019093250181
+:1011B0001092260110922701109228011092290105
+:1011C0000E947F340E9409264EC28091AB078C3466
+:1011D00009F041C28091AC07803509F03CC2809192
+:1011E000AD07811138C280E80E942C213CC2ED8AF3
+:1011F0008091AB07811136C040E550E06EEA73E0A4
+:1012000089EA97E00E94F627892B51F41092A907EA
+:1012100040E550E069EA77E08EEA93E00E943A28E0
+:1012200088E891E00E9417230E94FD200E948424F8
+:1012300082E891E00E94172386E50E940B328DE43C
+:101240000E940B3284E40E940B328CE20E940B322B
+:101250008FE00E94F1228CE20E940B3280E80E9413
+:10126000F1229FCE8D8A8D3309F0F5C183E0282FBE
+:1012700030E0F901E755F84F9081D901AA55B84FF0
+:101280009C938F5F90819111F2CF8D8B40E550E060
+:1012900069EA77E08EEA93E00E943A28E4C18091FF
+:1012A000AB07833509F0D7C18091AC07843509F0CD
+:1012B000D2C18091AD078D3309F0CDC18091AF07C8
+:1012C0008111C9C18091AE07843239F08A3239F078
+:1012D000833209F0C0C182E003C081E001C08FEF1A
+:1012E0000E94E9288BEA92E00E9417238FE292E0A5
+:1012F0000E9417230E9484240E948534B4C1ED8A81
+:101300008091AB0781111DC010E0812F0E947728CA
+:10131000811104C087E00E942A230EC08FE791E06C
+:101320000E941723812F0E94F1228DE30E940B322D
+:101330000E94FD200E9426231F5F123009F493C1F2
+:10134000E4CF911180C111E001C010E0BE016F5ED9
+:101350007F4FCE0145960E942B35811103C062E07C
+:10136000F62E81C1ED8981E08E0F8D8BF0E0E7557F
+:10137000F84F90819D3309F06EC1112371F1482F10
+:1013800050E0282F30E0F901E755F84F9081241BF9
+:10139000350BD901A755B84F9C938F5F9081911160
+:1013A000F0CF8D8B0E946F0B811150C169897A89B2
+:1013B0008B899C890E941A3A0E945837162F0E94E6
+:1013C0002434A1E51A9FC001112440E550E069EAE8
+:1013D00077E09D5F0E943A2831CEBE016F5F7F4F5C
+:1013E000CE0145960E942B35882309F4B8CFED89AC
+:1013F000F0E0E755F84F808181112DC1A988BA88A6
+:10140000CB88DC8820E030E04FE753E4C601B5012B
+:101410000E94F73818160CF41EC149805A806B8060
+:101420007C80C601B5010E945837062F20E030E0CD
+:10143000A901C301B2010E94D53687FD09C1043656
+:1014400008F440C0045680E0043098F510E0000F26
+:10145000111F000F111F823079F0F801833011F154
+:10146000813019F0E55CFE4F02C0E55BFE4F408223
+:10147000518262827382E4C0055A1E4F20E030E040
+:1014800040E752E4C301B2010E94373920E030E066
+:1014900040E752E40E943739F80160837183828308
+:1014A0009383CEC0E559FE4F77FA709477F8709425
+:1014B000DECF8F5F0A3008F4CEC0843009F4CBC091
+:1014C0000A50C2CFC301B2010E941A3A0E94583793
+:1014D00010E00132110508F0BEC0F801EC5CFF4FCE
+:1014E0000C94823A633008F4B9C060937B01A8C0C1
+:1014F00060937E01A5C060937C0102C060937D0172
+:101500000E9446319DC080919001662311F0846055
+:1015100095C08B7F93C080919001662311F0806409
+:101520008DC08F7B8BC080919001662311F0806805
+:1015300001C08F778093900180E00E94FD2180C0E0
+:1015400060937F017DC04092800150928101609242
+:1015500082017092830174C040928401509285018F
+:1015600060928601709287016BC080919001662322
+:1015700011F0816001C08E7F809390010E94AC21A8
+:101580005FC080919001662321F084FF6AC0806271
+:1015900055C08F7D53C080919001662311F0886003
+:1015A00001C0877F809390010E94032549C08091EC
+:1015B0009001662311F0806141C08F7C3FC0609331
+:1015C00091013EC040929201509293016092940129
+:1015D0007092950135C040929601509297016092A9
+:1015E0009801709299012CC070E070939B016093F8
+:1015F0009A0126C040929C0150929D0160929E01EA
+:1016000070929F011DC040928801509289016092A2
+:101610008A0170928B0108C040928C0150928D011A
+:1016200060928E0170928F010E94533409C08091A4
+:101630009001662311F0826001C08D7F809390013C
+:101640000E94992810C048E0F42E0DC0F82E0BC05F
+:1016500034E0F32E08C023E0F22E05C096E0F92E08
+:1016600002C08AE0F82E8F2D08C080912A01817275
+:1016700011F089E079CB0E946F0B0E94362376CB64
+:10168000002329F0893209F073CB0D7F71CB8132B1
+:1016900008F46ECB8F3209F46BCB883289F08B3330
+:1016A00089F01F3488F4212F30E01F5F9FE9980FE5
+:1016B000F901E755F84F9A3108F48052808358CBEE
+:1016C00002E056CB04E054CB01E052CB87E00E940D
+:1016D0002A238FEF0E94E9280E94722333CA2F9297
+:1016E0003F924F925F926F927F928F929F92AF92B2
+:1016F000BF92CF92DF92EF92FF920F931F93CF93FF
+:10170000DF93CDB7DEB7C355D1090FB6F894DEBF6E
+:101710000FBECDBF82E4ECEEF1E0DF011D928A95B1
+:10172000E9F78BE0E0EAF1E0ADEEB1E001900D9277
+:101730008A95E1F78091A907843211F00C948D19F4
+:1017400081E08093ED011092EE0183E08DA3B1E082
+:10175000BEA7212C312C1BAE1AAE1CAA1EA21AA6A3
+:10176000EE24E394F12C77247394EEE56E2EF1E6EB
+:101770004F2EF3E05F2E8DA1E82FF0E0E755F84FF4
+:10178000D080DD2009F49DC29FEB9D0D9A3110F0B1
+:101790000C945C198F5F8DA3BE016F5D7F4FCE01EE
+:1017A00085960E942B35882311F40C945E1989A02C
+:1017B0009AA0ABA0BCA0C501B4010E941A3A0E9435
+:1017C0005837162F70E080E090E00E9489379B0127
+:1017D000AC01C501B4010E94DF3520E030E048ECE7
+:1017E00052E40E9437390E94A4390E945837CB0135
+:1017F000E7E4DE1629F0FDE4DF1609F4E8C087C14E
+:10180000183209F4C3C020F5143168F4113108F01E
+:101810008AC0143009F451C008F463C01A3009F4C6
+:1018200044C00C946C191C3109F43FC050F41631BB
+:1018300010F00C946C1925E1211B2093EF0106E0B8
+:10184000AAC01E3191F1163209F44BC00C946C19E8
+:101850001D3309F495C0A0F4153379F140F41B321F
+:1018600009F478C0113309F475C00C946C191C3359
+:1018700010F00C946C1916531093F30109E08BC00F
+:101880001C3538F41A3508F053C0103581F10C942A
+:101890006C191C3551F01F3510F00C946C19262D65
+:1018A000211B2093EE0105E076C0009731F42AA5B4
+:1018B00021110C94601931E03AA71093EC01212F0B
+:1018C0002D7F2C3119F01C3509F062C0009721F0F2
+:1018D0000A9711F00C946C19610F6093EC0100E011
+:1018E0005FC04AA541110C946019A2E0AAA7109309
+:1018F000ED01163209F04EC08431910559F08E3158
+:10190000910541F08832910529F08233910511F05B
+:101910000C946C196AE070E00E944C3A6657609330
+:10192000ED0101E03DC011511093F10102E033C01F
+:10193000892B29F41A551093F00103E031C06A3065
+:10194000710511F00C946C191A3511F40C946C1982
+:1019500004E026C0BAA5B1110C946019113331F41A
+:101960001092F20108E0E3E0EAA71AC00A9711F02A
+:101970000C946C197092F20108E0F3E0FAA710C021
+:10198000892B11F00C946C190AE00AC007E003C01F
+:1019900000E001C001E0892B19F087E10C94941953
+:1019A000C701002E02C0880F991F0A94E2F72AADE2
+:1019B0003BAD28233923232B19F085E10C9494198E
+:1019C0002AAD3BAD282B392B3BAF2AAFD4CE892B88
+:1019D00021F71B3040F4173058F5133070F01630F3
+:1019E000C8F00C946C191237B9F1133709F474C0AC
+:1019F0001E3111F00C946C190AC0112321F0113022
+:101A000009F46FC004C083E08093F4016AC01093AE
+:101A1000F40167C0143021F0153031F080E101C0CD
+:101A200080E28093F60102C01092F6010CE05AC0E9
+:101A3000193071F08091F5011A3021F0183021F041
+:101A4000806803C08F7701C080648093F50102C075
+:101A50001092F5010DE046C080E1EAE1F1E0DE011F
+:101A6000519601900D928A95E1F74E0131E1830E76
+:101A7000911CB401CE0101960E94702168ECC62E23
+:101A800061E0D62ECE0101965C01D6016D917D916B
+:101A90008D919D916D011C962D913D914D915C9183
+:101AA0001F970E94E0359B01AC01F50160817181B7
+:101AB000828193810E94DF35D5016D937D938D9353
+:101AC0009D935D018A169B0601F7CE0101960E9447
+:101AD000CA220E94262306C081EB92E00E941723AF
+:101AE00001C00BE0C701002E02C0880F991F0A94A5
+:101AF000E2F72AAD3BAD28233923232B09F05DCF34
+:101B0000EAADFBADE82BF92BFBAFEAAF34CE8D2D60
+:101B1000DD0C990BAA0BBB0BFC01E354F109E83176
+:101B2000F10510F00C946C19EB5AFF4F0C94823AAB
+:101B300080922A0290922B02A0922C02B0922D0247
+:101B4000FEA1F860FEA30DE097C08092F80190928C
+:101B5000F901A092FA01B092FB0100E08DC08092E1
+:101B6000FC019092FD01A092FE01B092FF012CA910
+:101B700021602CAB01E080C08092000290920102B3
+:101B8000A0920202B09203023CA932603CAB02E098
+:101B900073C08092040290920502A0920602B09255
+:101BA00007024CA944604CAB03E066C010930C02E2
+:101BB00004E062C0C501B4010E94513760930D0278
+:101BC00070930E0280930F029093100205E054C0B0
+:101BD0008092110290921202A0921302B09214020B
+:101BE00006E04AC08092150290921602A092170257
+:101BF000B092180207E040C08092190290921A0237
+:101C0000A0921B02B0921C0208E036C020E030E037
+:101C10004FE753E4C501B4010E94F738181614F4D5
+:101C20000C94621910931D0209E026C080921E02D6
+:101C300090921F02A0922002B09221028EA1816098
+:101C40008EA30AE019C08092220290922302A092F1
+:101C50002402B09225029EA192609EA30BE00CC0CC
+:101C60008092260290922702A0922802B092290226
+:101C7000AEA1A460AEA30CE06701002E02C0CC0CA4
+:101C8000DD1C0A94E2F7C60182219321892B11F011
+:101C90000C946419C201002E02C0959587950A9490
+:101CA000E2F780FF0BC020E030E0A901C501B401DC
+:101CB0000E94D53687FF02C00C9466192C283D2857
+:101CC0005ACDBEA1BB2329F0EAA5E11102C0F2E082
+:101CD000FAA725FE0FC080910D0290910E02A091EF
+:101CE0000F02B091100281389649A849B10514F04D
+:101CF0000C9468193EA5332329F020FC02C00C94F3
+:101D0000721918C08091EE01813071F44AA54230F9
+:101D1000E1F58091ED018035C1F18823B1F120FC1E
+:101D200002C00C94721931C08091A10181112DC0A3
+:101D300020FE1BC08091EF01813039F523E333E3AE
+:101D40004BEC51E46091F8017091F9018091FA0136
+:101D50009091FB010E9437396093F8017093F9016B
+:101D60008093FA019093FB0110C08091AF01909194
+:101D7000B001A091B101B091B2018093F8019093AC
+:101D8000F901A093FA01B093FB0130FC10C08091DF
+:101D9000AB019091AC01A091AD01B091AE018093E7
+:101DA000190290931A02A0931B02B0931C02809117
+:101DB000EC01843039F426FC03C08CE10C949419B6
+:101DC000E89426F88091F1018823C9F0813059F414
+:101DD00029968FAF299725961FAE259792E02396D7
+:101DE0009FAF239717C029961FAE2997A2E025968B
+:101DF000AFAF2597B1E02396BFAF23970BC0E2E0CA
+:101E00002996EFAF2997F1E02596FFAF2597239606
+:101E10001FAE23978091EF01813021F02AA5233056
+:101E2000B9F52DC05CEEE52E51E0F52E00E010E096
+:101E30003EA1C32ED12CC601002E02C095958795D8
+:101E40000A94E2F780FF12C023E333E34BEC51E442
+:101E5000D701D2966D917D918D919C91D5970E94DD
+:101E60003739F70162AB73AB84AB95AB0F5F1F4F94
+:101E7000F4E0EF0EF11C04301105E9F6CFCF8091AC
+:101E8000F201813029F42EA1243011F00C946A194A
+:101E900080E1E8ECF1E0DE01119601900D928A9567
+:101EA000E1F78AAD9BAD91FD09C09091EC019AAB31
+:101EB0009A30C9F09C3509F4B1C018C18091F30182
+:101EC000873018F08DE10C9494199091A601891730
+:101ED00061F3BE016F5F7F4F0E94C828882311F411
+:101EE0000C948019E2CFAEA1A11103C08AE10C9439
+:101EF0009419C10180759927892B09F45ECF6091EF
+:101F000011027091120280911302909114020E94AA
+:101F10001A3A0E9458376730A8F680910C02843133
+:101F200041F0823011F00C946C1927FE02C00C9421
+:101F30006C19BFEA2B22662319F0615068AF03C009
+:101F4000E091F301E8AF6CEF71E088AD0E94C82822
+:101F5000882311F40C948019B0900C024090E80191
+:101F60005090E9016090EA017090EB010CEE11E0F5
+:101F700040EAC42E41E0D42EE12CF12CFEA18F2E9C
+:101F8000912CC4010E2C02C0959587950A94E2F716
+:101F900080FF36C024E1B2122AC0D601D8962D9116
+:101FA0003D914D915C91DB9758966D917D918D910E
+:101FB0009C915B970E94DF35F80122A933A944A9BF
+:101FC00055A90E94DF35D80150966D937D938D936E
+:101FD0009C935397B2E0EB1213C0A30192010E94AD
+:101FE000DF35F801608B718B828B938B09C0F80110
+:101FF00082A993A9A4A9B5A9808B918BA28BB38B3D
+:10200000FFEFEF1AFF0A0C5F1F4F24E0C20ED11C36
+:1020100034E0E316F10409F0B4CF37C14EA14423F4
+:1020200009F464CF4090E8015090E9016090EA0122
+:102030007090EB0120EA822E21E0922E8E010F5F3C
+:102040001F4F3CEEE32E31E0F32EC12CD12CA42EF9
+:10205000B12CC5010C2C02C0959587950A94E2F726
+:1020600080FF29C0D8012D913D914D915C91F401E3
+:10207000608D718D828D938D0E94DF35D701D29650
+:102080002D913D914D915C91D5970E94DF35F701DF
+:1020900062AB73AB84AB95ABF2E0CF1216C0A30179
+:1020A00092010E94DF35D701D2966D937D938D9377
+:1020B0009C93D5970AC0F40180AD91ADA2ADB3ADAC
+:1020C000F70182AB93ABA4ABB5ABFFEFCF1ADF0A3E
+:1020D00024E0820E911C0C5F1F4F34E0E30EF11CD4
+:1020E00044E0C416D10409F0B4CFCEC08AA5833031
+:1020F00071F49AA99E3109F48FC0953309F4BEC0DA
+:102100009C3109F0C1C06CEF71E086E088C0AEA1DF
+:10211000AA2379F3B091F001B8AF4090E801509054
+:10212000E9016090EA017090EB0190EAC92E91E01C
+:10213000D92E0CEE11E0FE0131965F01E12CF12C5D
+:102140008A2E912CC4010E2C02C0959587950A9475
+:10215000E2F780FD0BC0F601808D918DA28DB38DCD
+:10216000F80182AB93ABA4ABB5AB45C0FAA9F5338C
+:1021700009F441C028AD21112BC0D601D8962D916C
+:102180003D914D915C91DB97F50160817181828178
+:1021900093810E94E035D801D2962D913D914D91C9
+:1021A0005C91D5970E94E035F80162AB73AB84ABCC
+:1021B00095ABF2E0EF121FC0A30192010E94E0353F
+:1021C000D801D2966D937D938D939C93D59713C030
+:1021D000F601208D318D428D538DD801D2966D91AF
+:1021E0007D918D919C91D5970E94E035F80162AB6D
+:1021F00073AB84AB95ABFFEFEF1AFF0A24E0C20E7E
+:10220000D11C0C5F1F4F34E0A30EB11C44E0E41658
+:10221000F10409F097CF6DCF6CEF71E087E00E9479
+:10222000C828882311F40C9480198EA1811103C051
+:1022300018AE1AA62AC0A0EAB1E0ECEEF1E080E008
+:1022400090E04EA1242F30E0A901082E02C0559540
+:1022500047950A94E2F740FD0AC058964D915D916A
+:102260006D917C915B97408B518B628B738B019648
+:10227000149634968430910539F706C08091ED01AB
+:10228000823010F00C946E1918AE8091ED018AAB7B
+:10229000803529F49EA191110C94701983C2AAA5CE
+:1022A000A23009F07FC2BAA9B11104C0EEA1E111B8
+:1022B00079C22BC020E030E0A9016091F801709153
+:1022C000F9018091FA019091FB010E94D536882393
+:1022D00011F40C947219FAA9FC3809F44DC240F4B7
+:1022E000F230A9F0F330B1F0F13009F05BC20AC06E
+:1022F0002AA92E3809F440C208F43BC22F3809F449
+:1023000038C250C23EA131114DC21AA64BC24EA5D1
+:1023100044604EA78EA1882309F4E8CD23969FAD93
+:102320002397092F10E02596BFAD2597AB2FB0E07E
+:10233000BFAFAEAF81E090E07C0123960FAC239756
+:1023400002C0EE0CFF1C0A94E2F725960FAC25970D
+:1023500002C0880F991F0A94E2F7E82AF92AEEA131
+:102360008E2F90E08E219F21892B09F4BDC7F801A3
+:10237000EE0FFF1FEE0FFF1FE451FE4FD801AA0F13
+:10238000BB1FAA0FBB1FA854BE4F2D913D914D916D
+:102390005C9162A973A984A995A90E94DF352B01DC
+:1023A0003C01EEADFFADEE0FFF1FEE0FFF1FE4513E
+:1023B000FE4FAEADBFADAA0FBB1FAA0FBB1FA854E7
+:1023C000BE4F2D913D914D915C9162A973A984A955
+:1023D00095A90E94DF354B015C016101F0E8CF2235
+:1023E000DD2427FEF3C0E89427F840E150E06EE1D9
+:1023F00072E088EB91E00E94A23A892B11F481E20D
+:1024000093C78091EF018130B1F423E333E34BECC8
+:1024100051E460911502709116028091170290911B
+:1024200018020E9437396093150270931602809348
+:102430001702909318028091150290911602A091B4
+:102440001702B09118028CAB9DABAEABBFABA30132
+:102450009201C301B2010E94373960966CAF7DAF23
+:102460008EAF9FAF6097A5019401C501B4010E9492
+:10247000373964966CAF7DAF8EAF9FAF649720E025
+:1024800030E040E850E46CA97DA98EA99FA90E9484
+:1024900037392CA93DA94EA95FA90E94373960960A
+:1024A0002CAD3DAD4EAD5FAD60970E94DF356496BB
+:1024B0002CAD3DAD4EAD5FAD64970E94DF356B0135
+:1024C0007C0120E030E0A9010E94D53687FD0EC7CF
+:1024D000C701B6010E94D6396B017C0164962CAD10
+:1024E0003DAD4EAD5FAD649760966CAD7DAD8EAD8C
+:1024F0009FAD60970E94E0350E94D6399B01AC01E8
+:10250000C701B60190580E94DF366B017C019AA981
+:10251000933021F4F7FAF094F7F8F09420E030E0EB
+:10252000A9016CA97DA98EA99FA90E94D53687FF14
+:1025300011C0F7FAF094F7F8F0948CA99DA9AEA910
+:10254000BFA9B0588093150290931602A09317026A
+:10255000B0931802000F111F000F111F04501E4FDF
+:10256000A7019601C501B4010E9437399B01AC0156
+:10257000C301B2010E94DF3520E030E040E05FE3BC
+:102580000E943739D8016D937D938D939C93139757
+:102590000EAD1FAD000F111F000F111F04501E4F75
+:1025A000A7019601C301B2010E943739A501940128
+:1025B0000E94E03520E030E040E05FE30E943739E0
+:1025C000F8016083718382839383ECC03CA9232F3D
+:1025D00030E03DAB2CABE222F322EF2809F488C6B1
+:1025E00041EF24228091EF01813009F47AC0F80193
+:1025F000EE0FFF1FEE0FFF1FE450FE4FC080D18093
+:10260000E280F380A7019601C301B2010E94DF3589
+:102610002B013C01EEADFFADEE0FFF1FEE0FFF1FD4
+:10262000E450FE4F80819181A281B3818CAB9DAB40
+:10263000AEABBFAB9C01AD01C501B4010E94DF355B
+:102640004B015C01A3019201C301B2010E94373921
+:102650002B013C01A5019401C501B4010E94373949
+:102660009B01AC01C301B2010E94E0350E94D63942
+:102670004B015C01A7019601C701B6010E943739E1
+:102680006B017C012CA93DA94EA95FA9CA01B90122
+:102690000E9437399B01AC01C701B6010E94E035A9
+:1026A0000E94D6396B017C0160931502709316026B
+:1026B00080931702909318029B01AC01C501B401ED
+:1026C0000E94DF354B015C01E894B7F82AE037ED52
+:1026D00043EA5BE3C501B4010E94F738181664F1C0
+:1026E00061C09CEEE92E91E0F92E8CA99DA90C2CDD
+:1026F00002C0959587950A94E2F780FF12C023E304
+:1027000033E34BEC51E4D70150966D917D918D915F
+:102710009C9153970E943739F701608B718B828BA4
+:10272000938BFFEFCF1ADF0A24E0E20EF11C34E0B6
+:10273000C316D104D1F65BCF20E030E040E05FE388
+:10274000C501B4010E94F73818160CF458CE2FE6D4
+:1027500032E143E85AE3C701B6010E9437399B01D1
+:10276000AC01C501B4010E94F73818160CF447CE2D
+:1027700019C04EA540614EA78AA98E58823018F420
+:102780008EA588608EA79EA1992309F4AFCB40E166
+:1027900050E06EE172E088EB91E00E94A23A892B52
+:1027A00009F42DCE2EA52170AEA5C101A0FF02C057
+:1027B0008E7D02C08E7D9C7FBAA5B111937C892B42
+:1027C00009F098C58E010F5E1F4F89E0F801119244
+:1027D0008A95E9F7222309F44BC08AAD9BAD867B2D
+:1027E000892B09F089C58091EC01853311F08111A5
+:1027F00083C58091AB019091AC01A091AD01B091E6
+:10280000AE018D8B9E8BAF8BB88F9091A90180917B
+:10281000A801892B4091F8015091F9016091FA01CA
+:102820007091FB01498B5A8B6B8B7C8B8460898FF9
+:102830008091900185FF06C08EE192E00E943521D3
+:1028400081115CC5B8018EE192E00E94290280915D
+:102850002A01811157C50E948D26892B09F452C582
+:1028600080E280932A010E9470290E94991C4AC527
+:1028700080919001F090A90181FF26C08AA9815022
+:10288000833018F0FEA5F064FEA72EA1222339F0B4
+:102890003AA5323021F44EA540684EA715C080E11C
+:1028A000F81212C08091A00181509EA590748330CF
+:1028B00030F4992349F09EA590629EA705C091111E
+:1028C00003C0AEA5A062AEA780910D0290910E024A
+:1028D000A0910F02B09110028093B4019093B501C2
+:1028E000A093B601B093B7018091EE018093A1014E
+:1028F000882311F088E0898F8091F8019091F90187
+:10290000A091FA01B091FB018093AF019093B001C7
+:10291000A093B101B093B201898B9A8BAB8BBC8B26
+:102920008090190290901A02A0901B02B0901C0295
+:10293000A50194016091AB017091AC018091AD0152
+:102940009091AE010E94D536BEA5B074EB2E8111D8
+:1029500003C0EEA5E5FF20C0FF2071F0FEA5F7FD46
+:102960000BC0EE2021F040E050E0BA0102C0B501FA
+:10297000A4018F2D0E9435348091190290911A0282
+:10298000A0911B02B0911C028093AB019093AC010B
+:10299000A093AD01B093AE01E1100CC08091AB01EA
+:1029A0009091AC01A091AD01B091AE018D8B9E8B49
+:1029B000AF8BB88F80911D028093B3018091F60197
+:1029C0009091A901981751F04D895E896F89788D92
+:1029D0000E9435348091F6018093A901998D8091F0
+:1029E000A901892B898FF090F5018091A8018F159D
+:1029F00069F080912A01823029F00E9424348F2DC1
+:102A00000E9427328091F5018093A801998D8091D1
+:102A1000A801892B898F8091EC01843099F4C090B2
+:102A20001102D0901202E0901302F09014028091F3
+:102A30002A01823039F00E94243440E0C701B601F7
+:102A40000E94DA258091F1018093A4018091EF0129
+:102A50008093A2012AA5233071F58091F201809321
+:102A6000A501811108C0109226021092270210922F
+:102A7000280210922902C0902602D0902702E090EE
+:102A80002802F0902902A70196016091E801709157
+:102A9000E9018091EA019091EB010E94D5368823EB
+:102AA00051F0C092E801D092E901E092EA01F0927F
+:102AB000EB010E94AC218091F3019091A60198173F
+:102AC00069F08093A60180E1FE013196A8ECB1E0A7
+:102AD00001900D928A95E1F70E94AC218091F0015E
+:102AE0008093A3018091EC018632F1F138F48C31AE
+:102AF00009F18E31F9F08A3069F04FC08C35D1F18F
+:102B0000863609F441C0883209F047C068EB71E0AD
+:102B100087E02DC06CEF71E088AD0E94B4288091F1
+:102B2000A60138AD381339C080E1ECEFF1E0A8EC34
+:102B3000B1E025C0898D8160898F4AA5442329F0A1
+:102B4000B8018EE192E00E942902B8018CEF91E079
+:102B50000E94290280E1ECEFF1E0A8EBB1E00190E6
+:102B60000D928A95E1F719C068EB71E086E00E944A
+:102B7000B42813C080E1EEE1F2E0A8EDB1E00190ED
+:102B80000D928A95E1F707C080E1E8EDF1E0DF0101
+:102B90001D928A95E9F70E94AC218091ED01809306
+:102BA000A001803509F43AC3BAA5B23009F036C3A2
+:102BB000813021F4BE016F5E7F4F06C0811109C0D4
+:102BC000898D8160898FB8018EE192E00E9429028F
+:102BD0001CC38250823008F0B1C2809115029091DE
+:102BE0001602A0911702B09118028AA79BA7ACA762
+:102BF000BDA72396AFAD239794E0A99F7001112440
+:102C0000F701E854FE4FFBABEAAB970124503E4F6F
+:102C100039AF28AFD9014D905D906D907C902081A7
+:102C2000318142815381C301B2010E94E0356AAF14
+:102C30007BAF8CAF9DAF2596EFAD2597B4E0EB9FB2
+:102C400080011124980128543E4F3DAB2CABC801A4
+:102C500084509E4F24969FAF8EAF2497DC018D90B9
+:102C60009D90AD90BC90F9012081318142815381CA
+:102C7000C501B4010E94E03528966CAF7DAF8EAFE0
+:102C80009FAF289777FA709477F87094D501C401B4
+:102C9000B0588EA39FA3A8A7B9A7F701E25EFD4F86
+:102CA0002AAD3BAD4CAD5DAD608171818281938178
+:102CB0000E94DF356B017C01F801E25EFD4F289632
+:102CC0002CAD3DAD4EAD5FAD2897608171818281A5
+:102CD00093810E94DF354B015C01A7019601C3017E
+:102CE000B2010E94373922966CAF7DAF8EAF9FAF95
+:102CF0002297A50194016EA17FA188A599A50E94A4
+:102D000037399B01AC0122966CAD7DAD8EAD9FAD88
+:102D100022970E94E03522966CAF7DAF8EAF9FAFB9
+:102D20002297A5019401C301B2010E9437394B01DA
+:102D30005C01A70196016EA17FA188A599A50E94BB
+:102D400037399B01AC01C501B4010E94DF352296E1
+:102D50002CAD3DAD4EAD5FAD22970E945F366B014D
+:102D60007C019EA592FF13C02DEB37E346E055EBA7
+:102D7000C701B6010E94F73887FD1DC02BED3FE06B
+:102D800049EC50E4C701B6010E94DF3512C02DEBBB
+:102D900037E346E055E3C701B6010E94D536181661
+:102DA00054F02BED3FE049EC50E4C701B6010E941E
+:102DB000E0356B017C018090840190908501A090AA
+:102DC0008601B09087012AA53BA54CA55DA5CA0147
+:102DD000B9010E94E035A50194010E94DF35A501EB
+:102DE00094010E9437390E94D6394B015C0120E0E2
+:102DF00030E040E05FE3C701B6010E9437392AA501
+:102E00003BA54CA55DA50E9437399F77A50194018C
+:102E10000E94DF360E94C4370E9458377FAF6EAFE2
+:102E2000DB01AB2B09F4C6CE198D4B01A12CB12CC3
+:102E300013FF12C0C501B4010E9487379B01AC018A
+:102E400069897A898B899C890E943739698B7A8B49
+:102E50008B8B9C8B177F198FC501B4010E9487371C
+:102E60004B015C019B01AC01C701B6010E94DF363A
+:102E70006EA77FA788AB99AB2996EFAD2997B4E0F1
+:102E8000EB9FC00111248C0108541E4FFC01E25E2F
+:102E9000FD4FD8012D913D914D915C9160817181E3
+:102EA000828193810E94DF35A50194010E94DF3663
+:102EB00064966CAF7DAF8EAF9FAF64972EA53FA594
+:102EC00048A959A9CA01B9010E9437399B01AC012F
+:102ED00060E070E080E090E40E94DF356B017C01EF
+:102EE0002BEA3AEA4AE25EE36EA57FA588A999A992
+:102EF0000E9437394B015C0120E030E040E850E4AB
+:102F0000C701B6010E94E0359B01AC01C501B401C7
+:102F10000E9437392C966CAF7DAF8EAF9FAF2C9748
+:102F200020E030E040E05FE3C701B6010E9437399E
+:102F300060966CAF7DAF8EAF9FAF60971AA62224CC
+:102F40002394312CEEADFFADE215F30509F432CE3A
+:102F5000BAA5BC3008F048C02EA13FA148A559A58C
+:102F600060966CAD7DAD8EAD9FAD60970E94373998
+:102F70006B017C01A30192012C966CAD7DAD8EADF1
+:102F80009FAD2C970E943739A70196010E94E0352A
+:102F90007B016C01A301920160966CAD7DAD8EAD9D
+:102FA0009FAD60970E9437394B015C012EA13FA174
+:102FB00048A559A52C966CAD7DAD8EAD9FAD2C97D7
+:102FC0000E9437399B01AC01C501B4010E94DF3575
+:102FD0002B013C01EAA5EF5FEAA7470156018EA24B
+:102FE0009FA2A8A6B9A667C0B10180E090E00E94A8
+:102FF00087372EA53FA548A959A90E9437396B01EB
+:103000007C010E94DA366EA37FA388A799A7C70127
+:10301000B6010E94C8394B015C01A8ADB9ADCD9095
+:10302000DD90ED90FC90F7FAF094F7F8F094249688
+:10303000AEADBFAD24978D919D910D90BC91A02D0B
+:103040008AA79BA7ACA7BDA7A70196016EA17FA1E8
+:1030500088A599A50E9437392B013C012AA53BA5DB
+:103060004CA55DA5C501B4010E9437399B01AC0197
+:10307000C301B2010E94E0352B013C01A70196017A
+:10308000C501B4010E9437396B017C012AA53BA51B
+:103090004CA55DA56EA17FA188A599A50E94373991
+:1030A0009B01AC01C701B6010E94DF356EA37FA36F
+:1030B00088A799A71AA6A30192016AAD7BAD8CAD32
+:1030C0009DAD0E94E035EAA9FBA9608371838283EC
+:1030D00093832EA13FA148A559A528966CAD7DAD3F
+:1030E0008EAD9FAD28970E94E035ACA9BDA96D9328
+:1030F0007D938D939C93139764962CAD3DAD4EAD0F
+:103100005FAD6497F80160817181828193810E9433
+:10311000E035D8016D937D938D939C931397BE01F9
+:103120006F5E7F4F88EB91E00E94290280912B0116
+:1031300081116BC0BFEF2B1A3B0A04CF898D8460CD
+:10314000898F80912A01823009F45FC00E94243463
+:1031500080912B01811162C0FEA5F071FF2E1092AB
+:103160002F012EA523FB882780F90E94FD2186B11F
+:10317000807490910801891749F084E00E941C2115
+:103180000E94123380E00E94FD2148C0B8018EE108
+:1031900092E00E94290281E08093190182E00E945E
+:1031A0002C210E94123380912B01811137C0809114
+:1031B0002A018111F6CF80911901813081F4FF201D
+:1031C00051F080E1EAE1F1E0A9E0B1E001900D9277
+:1031D0008A95E1F707C085E00E941C2103C081E0C9
+:1031E00080932F011092190180E00E94FD210E941E
+:1031F00012330E9487310E949D260E9409260E9458
+:10320000892480912F0181118CC080E1EEE1F2E0F0
+:10321000A8EBB1E001900D928A95E1F78091F4015D
+:103220008093A701882309F47FC00E9424348091F1
+:10323000A70190912A01833041F49230D1F188E0C6
+:103240000E942C210E94123334C081E08093A0019F
+:103250001092A4011092A3011092A1011092A60154
+:103260001092A9011092A80184E680933101809305
+:10327000320180933301923091F068EC71E080E08C
+:103280000E94C8288823E1F10E94AC2140E050E070
+:10329000BA0180E00E94EE3280E00E9427328BEA81
+:1032A00092E00E94172382E492E00E9417230E947A
+:1032B00084241092A7012FC081E036C082E034C080
+:1032C00088E132C086E230C089E12EC084E02CC0A3
+:1032D0008BE12AC085E228C084E126C08EE124C0AB
+:1032E0008FE122C086E120C080E21EC082E21CC0C5
+:1032F00083E21AC084E218C080E116C08FE014C0D7
+:1033000087E012C080E1EEE1F2E0A8EBB1E00190CD
+:103310000D928A95E1F780E007C01DA21EA60C94CD
+:10332000A90B0E947F347ACFCD5ADF4F0FB6F894A5
+:10333000DEBF0FBECDBFDF91CF911F910F91FF90E8
+:10334000EF90DF90CF90BF90AF909F908F907F9045
+:103350006F905F904F903F902F90089580916F00F5
+:103360008D7F80936F0080918100887F8160809342
+:1033700081001092BE0580917E0190912A018F3FBD
+:1033800039F42091B905211103C0903881F402C0AD
+:10339000943069F090E0009741F0EFE9FFE0319759
+:1033A000F1F700C000000197F6CF81E001C080E096
+:1033B0009091900192FF02C091E08927882311F03B
+:1033C0005F9A08955F9808951F920F920FB60F921B
+:1033D00011242F933F934F935F936F937F938F931A
+:1033E0009F93AF93BF93EF93FF938091BE0581119D
+:1033F0008FC285B19091D2059771887E892B85B94E
+:103400008BB19091D1059C778378892B8BB9809172
+:10341000D00586BD82E085BD81E08093BE057894AD
+:103420008091E8059091E905892B09F0D7C080913A
+:10343000EA0590911506981709F4BCC0E091EA05D9
+:103440002E2F30E087E0E89FF0011124E551FA4F7C
+:10345000F093E905E093E8058281938190938900D8
+:1034600080938800808191819093E4058093E305A7
+:10347000E4818091E5058E17A1F1E093E50586E1F1
+:10348000E89FF0011124EA5EF94FF093E705E0931D
+:10349000E60580899189A289B389B695A795979504
+:1034A00087958093CB059093CC05A093CD05B093E1
+:1034B000CE058093C7059093C805A093C905B09326
+:1034C000CA058093C3059093C405A093C505B09326
+:1034D000C6058093BF059093C005A093C105B09326
+:1034E000C205E091E605F091E70594898091BD055C
+:1034F00089278093D20587E0829FD001839FB00DFA
+:103500001124A551BA4F15968C9115974081518180
+:1035100062817381082E04C076956795579547950B
+:103520000A94D2F74093D3055093D4056093D50500
+:103530007093D6054481558166817781082E04C039
+:1035400076956795579547950A94D2F74093D70596
+:103550005093D8056093D9057093DA05408551855D
+:1035600062857385082E04C07695679557954795B3
+:103570000A94D2F74093DB055093DC056093DD0598
+:103580007093DE05448555856685778504C07695FC
+:103590006795579547958A95D2F74093DF055093E5
+:1035A000E0056093E1057093E20516968C910E9408
+:1035B000E03214C00E94AE19E091E605F091E705F3
+:1035C0008589882339F01092B3008091B0008F77FD
+:1035D0008093B00084E00E942C219AC1809119014F
+:1035E0008130B1F486B1807490910801891781F01F
+:1035F0001092190180E1EAE1F1E0A9E0B1E0019067
+:103600000D928A95E1F78091B80580648093B805A2
+:103610001092D1054091BF055091C0056091C10540
+:103620007091C2058091D3059091D405A091D505E4
+:10363000B091D605840F951FA61FB71F8093BF05B5
+:103640009093C005A093C105B093C205E091E60533
+:10365000F091E705408951896289738948175907B4
+:103660006A077B0768F524E02093D10540895189DA
+:1036700062897389841B950BA60BB70B8093BF05DA
+:103680009093C005A093C105B093C2052489809191
+:103690001A0190911B01A0911C01B0911D0120FF06
+:1036A00004C00197A109B10903C00196A11DB11D74
+:1036B00080931A0190931B01A0931C01B0931D01EC
+:1036C0004091C3055091C4056091C5057091C60530
+:1036D0008091D7059091D805A091D905B091DA05D0
+:1036E000840F951FA61FB71F8093C3059093C40531
+:1036F000A093C505B093C605408951896289738935
+:10370000481759076A077B0778F52091D10528618A
+:103710002093D1054089518962897389841B950B57
+:10372000A60BB70B8093C3059093C405A093C50562
+:10373000B093C605248980911E0190911F01A0912C
+:103740002001B091210121FF04C00197A109B10915
+:1037500003C00196A11DB11D80931E0190931F010E
+:10376000A0932001B09321014091C7055091C80555
+:103770006091C9057091CA058091DB059091DC05C7
+:10378000A091DD05B091DE05840F951FA61FB71F20
+:103790008093C7059093C805A093C905B093CA0547
+:1037A0004089518962897389481759076A077B07DD
+:1037B00078F52091D10520622093D1054089518967
+:1037C00062897389841B950BA60BB70B8093C70581
+:1037D0009093C805A093C905B093CA052489809128
+:1037E000220190912301A0912401B091250122FF93
+:1037F00004C00197A109B10903C00196A11DB11D23
+:103800008093220190932301A0932401B09325017A
+:103810004091CB055091CC056091CD057091CE05BE
+:103820008091DF059091E005A091E105B091E2055E
+:10383000840F951FA61FB71F8093CB059093CC05CF
+:10384000A093CD05B093CE054089518962897389D3
+:10385000481759076A077B0778F52091D10520643E
+:103860002093D1054089518962897389841B950B06
+:10387000A60BB70B8093CB059093CC05A093CD05F9
+:10388000B093CE0524898091260190912701A091C3
+:103890002801B091290124FF04C00197A109B109B1
+:1038A00003C00196A11DB11D8093260190932701AD
+:1038B000A0932801B093290180912A01843039F422
+:1038C0009091D1058091300189238093D105809119
+:1038D000E3059091E40501979093E4058093E30557
+:1038E000892B69F41092E9051092E8058091EA05A8
+:1038F0008F5F8093EA05863011F41092EA0590916B
+:10390000D1058091BC0589278093D1051092BE0511
+:10391000FF91EF91BF91AF919F918F917F916F91A7
+:103920005F914F913F912F910F900FBE0F901F907D
+:1039300018958091900182FF02C05F9A01C05F9844
+:103940008091BC058093D10580917B01880F8450C4
+:1039500081958093D00580916F00826080936F0085
+:1039600008953F924F925F926F927F928F929F92B3
+:10397000AF92BF92CF92DF92EF92FF920F931F937D
+:10398000CF93DF93CDB7DEB7E0970FB6F894DEBFE5
+:103990000FBECDBF1091B9051123C9F181E080930D
+:1039A0002A01123029F483E193E00E94172307C013
+:1039B0008CE093E00E941723812F0E94F1220E9445
+:1039C000262385EF91E0019739F0EFE9FFE0319789
+:1039D000F1F700C00000F7CF1150123030F08FB770
+:1039E000F8941092B9058FBF12C08BEA92E00E9442
+:1039F000172389E992E00E9417230E94842480E122
+:103A00000E9422218091B80584FFFCCFE8CF10915D
+:103A1000B805112309F4B4C214FF04C081E08093F7
+:103A20002B01D3C310FFB8C180E1EAE1F1E0DE0170
+:103A3000919601900D928A95E1F7BE016F5D7F4FDF
+:103A4000CE0141960E9470218CE30E940B3280913E
+:103A50002A018830E1F058F4813099F1A8F08230E1
+:103A600099F1843009F043C081E893E03EC0803290
+:103A700029F118F4803171F03AC0803441F1803876
+:103A8000B1F589E693E031C084E993E02EC080E986
+:103A900093E02BC080912C0187FD10C08AE893E051
+:103AA0000E94172380912C0180FF06C080E31AC07A
+:103AB00082FF17C085FFFACF81E314C086E893E048
+:103AC00014C08BE793E011C085E793E00EC08FE64A
+:103AD00093E00E94172380912C0183FFE9CF83E3B9
+:103AE00001C082E30E940B3202C00E941723809122
+:103AF0007F01F82FF170BF2E80FF07C08091360143
+:103B0000882319F082E693E053C04090E80150907A
+:103B1000E9016090EA017090EB0148EC842E41E0ED
+:103B2000942E9E012F5F3F4F7901CE0141966C018B
+:103B300001E0F40161917191819191914F0124858E
+:103B40003585468557850E94E035F701608371838E
+:103B500082839383033099F4A30192010E94E0359C
+:103B6000F7016083718382839383BB2051F094E0DB
+:103B7000E90EF11C0F5FE4E0CE0ED11CDACFB110DC
+:103B800011C0F7012081318142815381F6016081AA
+:103B90007181828193810E94DF35F6016083718398
+:103BA00082839383043019F7B110ACCF8BE593E097
+:103BB0000E941723CE0141960E94CA2280917F0164
+:103BC00081FF1FC086E593E00E9417239091930523
+:103BD00080919205981710F0815F01C08150891B78
+:103BE0000E94F1228CE20E940B3280912F02909170
+:103BF0002E02981710F0805801C08150891B0E9436
+:103C0000F12281E593E00E94172380912A018C77AD
+:103C100049F060919B0670919C0680919D06909161
+:103C20009E0603C060E070E0CB010E94AC228CE2F3
+:103C30000E940B32609137017091380180913901F7
+:103C400090913A0140E00E940F220E94E224F82E57
+:103C50000E94B121082F86B1982F9074809108019D
+:103C6000E92EE8268F2D802B8E2959F18CE493E0E4
+:103C70000E941723EE2019F080E50E940B32FF20EE
+:103C800079F0F0FE03C088E50E940B32F1FE03C01C
+:103C900089E50E940B32F2FE03C08AE50E940B32D6
+:103CA000002379F000FF03C082E50E940B3201FF80
+:103CB00003C088E40E940B3202FF03C083E50E9428
+:103CC0000B3280913601882321F081508093360198
+:103CD00018C080912A018C7711F08DE101C089E034
+:103CE0008093360180913501811103C081E080937A
+:103CF000350186E493E00E941723CE0101960E94CD
+:103D0000CA2280913501882321F0815080933501AA
+:103D10003BC080912A018C7711F083E101C089E0DA
+:103D20008093350181E493E00E9417238091310153
+:103D30000E94F1228CE20E940B32809132010E949B
+:103D4000F1228CE20E940B32809133010E94F12219
+:103D500008B108700E943B32F82E011102C088237E
+:103D600099F08DE393E00E941723002319F083E577
+:103D70000E940B32F6FE03C086E40E940B32F7FE6F
+:103D800003C08DE40E940B328EE30E940B320E942E
+:103D9000262381E00E942221812F887E09F467C0BA
+:103DA00080912A01982F937009F050C08872A1F079
+:103DB00080912C01807C81F40E94223182E08093EA
+:103DC0002E0180912A01803239F417FDFCC18091C7
+:103DD0002C01806880932C0180912A01811103C0FD
+:103DE00091E090932C0116FF07C085FD05C09091CE
+:103DF0002C01906490932C0113FF05C0807E19F470
+:103E000080E180932A0115FF21C08BEA92E00E9495
+:103E1000172387E592E00E9417230E948424809153
+:103E20002C0187FD0EC090912A01903439F483FF54
+:103E3000C6C1837E826080932C01C1C1903809F095
+:103E4000BEC180912C01806280932C0117FF0CC0B1
+:103E500080912A01813029F480912C018560809322
+:103E60002C0180E880932A0188EE0E94222111FF14
+:103E700044C0812F8876F1F580912A01803471F455
+:103E800080912C0185FD0AC084FF03C010922A0195
+:103E900005C082FF03C0886080932C0180912A01B5
+:103EA000882381F084FF26C090912C0190FF22C0CE
+:103EB000803141F480913401882321F0886080931F
+:103EC000340118C010922E010E948D26892B71F0AA
+:103ED00080912C0186FD0AC010922C0188E080930D
+:103EE0002A010E9470290E94991C04C010922C0182
+:103EF00010922A0182E00E94222112FF41C080918B
+:103F00002A01807D90912C018823D9F080912D0188
+:103F1000811117C097FD15C00E9422318091920532
+:103F2000809330020E94AE2680912E0181FF05C051
+:103F300090912C01916090932C01897F80932E01A8
+:103F40001CC097FF0AC010922E010E949D260E945D
+:103F500087310E947F340E94092680912C0185FFC1
+:103F600008C08F77816080932C0180E480932A01C0
+:103F700004C010922C0110922A0184E00E94222198
+:103F80009091BA05992309F4ABC08FB7F8941092B9
+:103F9000BA058FBF4091310190FD02C0842F01C04E
+:103FA00084E691FD865F92FD8A5093FD8F5F94FDBC
+:103FB0008150893C08F088EC8A3008F48AE030911E
+:103FC000320195FD02C0232F01C024E696FD22E3B5
+:103FD00097FD29E1481303C0231709F481C080939A
+:103FE00031012093320110923501109192053090E9
+:103FF000930589E9D82E96E7E92E26E9F22E0EE7F9
+:1040000036E3432E131509F457C0812E912C489C9A
+:10401000C001499C900D11249C012E5C3D4F59011B
+:10402000C9010E941A26562E672E782EC92E2D2DD4
+:104030003E2D4F2D502F0E94F73818164CF42D2D81
+:104040003E2D4F2D502F6D2D7E2D8F2D902F08C082
+:10405000252D362D472D5C2D652D762D872D9C2DFC
+:104060000E943739F501628F738F848F958F489C3A
+:10407000C001499C900D11249C012E5C3D4F79019B
+:10408000F90186A097A0A0A4B1A4A5019401628D16
+:10409000738D848D958D0E94F73818162CF4F701D6
+:1040A000828E938EA48EB58E1F5F103109F410E0BE
+:1040B000D52C73010C2DA6CF8D2D9E2DAF2DB02F9D
+:1040C0008093B4059093B505A093B605B093B7055A
+:1040D0000E94223180919205809330020E94AE2688
+:1040E0000091BB05002309F463C08FB7F8941092C8
+:1040F000BB058FBF9091330100FD02C0892F01C025
+:1041000084E601FD865F02FD8A5003FD8F5F04FD9A
+:104110008150893C08F088EC8A3008F48AE09817CE
+:10412000D1F08093330180912A0181110DC040911B
+:10413000AB015091AC016091AD017091AE018091E5
+:10414000A9010E94EE3205C080912E018860809303
+:104150002E011092350180912A0105FF0DC080319A
+:1041600059F490913401911102C092E003C090FF84
+:1041700003C0946090933401902F907CC9F0882301
+:1041800011F08873A9F01091A80107FF05C017FF6F
+:1041900002C01F7701C0106806FF05C016FF02C0ED
+:1041A0001F7B01C01064812F0E9427321093A80149
+:1041B00080912A018C7F49F00E94702906C080E41A
+:1041C00080932A013ECE80E20ECEE0960FB6F894A0
+:1041D000DEBF0FBECDBFDF91CF911F910F91FF903A
+:1041E000EF90DF90CF90BF90AF909F908F907F9097
+:1041F0006F905F904F903F900895CF93DF93C9EAFF
+:10420000D7E08991882319F00E940B32FACFDF9111
+:10421000CF9108959FB7F8942091BB05822B80938E
+:10422000BB059FBF08959FB7F8942091BA05822BD4
+:104230008093BA059FBF08959FB7F8948093B905FE
+:104240009FBF08952FB7F8949091B8058095892362
+:104250008093B8052FBF08959FB7F8942091B805B3
+:10426000822B8093B8059FBF0895AF92BF92CF92E3
+:10427000DF92EF92FF920F931F93CF93DF93EC01A6
+:104280000BE611E05C018CE0A80EB11CC990D9903E
+:10429000E990F99020E030E0A901C701B6010E9441
+:1042A000F73818168CF0F801219131914191519114
+:1042B0008F01C701B6010E94D53687FD05C0CA151A
+:1042C000DB0521F780E001C081E0DF91CF911F91F4
+:1042D0000F91FF90EF90DF90CF90BF90AF90089537
+:1042E0008F929F92AF92BF92CF92DF92EF92FF9206
+:1042F0000F931F93CF93DF93EB012BE3E22E21E08B
+:10430000F22E8C016B0180E1C80ED11C699179916C
+:1043100089919991F70181909190A190B1907F013D
+:104320000E948937A50194010E94DF36F80161934C
+:104330007193819391938F01CC15DD0539F7DF914E
+:10434000CF911F910F91FF90EF90DF90CF90BF9092
+:10435000AF909F908F9008950E9424341092360160
+:10436000089596B19095892F877059F091FF02C0FA
+:1043700082E001C080E090FD816092FF03C0846014
+:10438000089580E008951F920F920FB60F921124A6
+:104390002F933F934F935F936F937F938F939F934D
+:1043A000AF93BF93CF93EF93FF930E94B121C82F98
+:1043B000882389F080FD0E948534C2FF05C080916A
+:1043C000B80582608093B805C1FF05C08091B8052B
+:1043D00088608093B805FF91EF91CF91BF91AF9125
+:1043E0009F918F917F916F915F914F913F912F910D
+:1043F0000F900FBE0F901F9018951092080190918A
+:10440000900197FD03C090E490930801882331F058
+:104410009091080180E489278093080108958F9284
+:104420009F92AF92BF92CF92DF92EF92FF920F9343
+:104430001F93CF93DF93CDB7DEB72D970FB6F894C8
+:10444000DEBF0FBECDBF6B017C01042F20E030E04A
+:10445000A9010E94D53687FF07C08DE20E940B326A
+:10446000F7FAF094F7F8F094102F123060F020E093
+:1044700030E048EC52E4C701B6010E9437396B01C5
+:104480007C011250F2CF00FF0AC020E030E040E291
+:1044900051E4C701B6010E9437396B017C0120E06D
+:1044A00030E040E05FE3C701B6010E94E0350E94C2
+:1044B000513710E03AE0832E912CA12CB12C6115DC
+:1044C00071058105910591F0EE24E394F12CEC0E39
+:1044D000FD1EE10EF11CA50194010E94603A605D91
+:1044E000F7016083B901CA011F5FE9CF80E31017AC
+:1044F00048F4E1E0F0E0EC0FFD1FE10FF11D8083D7
+:104500001F5FF5CF101313C0FE01E10FF11D80E313
+:1045100081831F5F0CC0101303C08EE20E940B3218
+:10452000FE01E10FF11D80810E940B32115011112B
+:10453000F2CF2D960FB6F894DEBF0FBECDBFDF9140
+:10454000CF911F910F91FF90EF90DF90CF90BF9090
+:10455000AF909F908F9008952091900120FF08C008
+:104560002BE832E441E25DE30E94373941E001C0CB
+:1045700040E00C940F222091900120FF08C02BE80E
+:1045800032E441E25DE30E94373944E001C043E098
+:104590000C940F22EF92FF920F931F93CF9300E0A2
+:1045A00011E07C01C0E0F80181918F010E940B3283
+:1045B0008AE30E940B32F70161917191819191918F
+:1045C0007F010E94BB22C33019F080E20E940B32AF
+:1045D000CF5FC43041F7CF911F910F91FF90EF90C3
+:1045E0000895CF93DF93843630F06AE00E94403A1A
+:1045F000C0E3C90F03C0C0E08A3030F06AE00E9417
+:10460000403AD0E3D90F01C0D0E0805D0E940B3268
+:10461000DD2319F08D2F0E940B32CC2329F08C2F33
+:10462000DF91CF910C940B32DF91CF910895CF930E
+:10463000DF93FC018491EF012196882321F00E94F1
+:104640000B32FE01F7CFDF91CF9108958EEF93E00B
+:104650000C941723CF93C82F8CE094E00E9417236B
+:104660008C2F0E94F122CF910C942623811104C03B
+:1046700081E094E00C9417230C942A23CF92DF92CC
+:10468000EF92FF92CF93DF93D82F6A017B01C22F65
+:1046900084E20E940B328D2F0E94F1228DE30E9452
+:1046A0000B324C2FC701B6010E940F22DF91CF9130
+:1046B000FF90EF90DF90CF900C942623CF93DF9361
+:1046C000D82FC62F84E20E940B328D2F0E94F12238
+:1046D0008DE30E940B328C2F0E94F122DF91CF914B
+:1046E0000C942623EF92FF920F931F93CF93DF93A7
+:1046F00060917B0170E080E00E945E2360917E010A
+:1047000070E081E00E945E2360917C0170E082E0B5
+:104710000E945E2360917D0170E083E00E945E2331
+:104720006091900162FB662760F970E084E00E946E
+:104730005E236091900166FB662760F970E085E07A
+:104740000E945E2380919001082E000C990B692F26
+:10475000661F6627661F70E086E00E945E236091F8
+:104760007F0170E08AE00E945E23409180015091B9
+:104770008101609182017091830123E08BE00E94AE
+:104780003E23409184015091850160918601709192
+:10479000870123E08CE00E943E23609190016170CC
+:1047A00070E08DE00E945E236091900165FB6627BA
+:1047B00060F970E084E10E945E236091900163FBE8
+:1047C000662760F970E085E10E945E2360919001A8
+:1047D0006295617070E086E10E945E2360919101B4
+:1047E00070E087E10E945E23409192015091930115
+:1047F000609194017091950123E088E10E943E232D
+:1048000040919601509197016091980170919901A2
+:1048100023E089E10E943E2360919A0170919B01FF
+:104820008AE10E945E2340919C0150919D0160911C
+:104830009E0170919F0123E08BE10E943E234091F5
+:1048400088015091890160918A0170918B0120E06B
+:104850008EE10E943E2340918C0150918D01609128
+:104860008E0170918F0120E08FE10E943E236091C4
+:1048700090016695617070E080E20E945E23D4E64C
+:10488000F12C0BE311E084E0E82EED0ECD2F82E059
+:10489000F81691F0E3E0FE16E9F0F1E0FF1631F0D2
+:1048A000F80140815181628173811AC0F801408909
+:1048B00051896289738914C020E030E041E655E4F3
+:1048C000F80160A171A182A193A10E94DF36AB0122
+:1048D000BC0106C0F80140A951A962A973A970588A
+:1048E00023E08C2F0E943E230C5F1F4FCF5FEC1202
+:1048F000CECFD65FF394DC3821F6DF91CF911F91B4
+:104900000F91FF90EF9008958DE50E940B320C946B
+:104910002623CF93DF93CDB7DEB760970FB6F89419
+:10492000DEBF0FBECDBF86E094E00E94172369E092
+:1049300071E0CE0101960E947021CE0101960E9485
+:10494000CA228AE30E940B3280912F010E94F12239
+:104950000E94842460960FB6F894DEBF0FBECDBFD0
+:10496000DF91CF9108951F920F920FB60F921124ED
+:104970002F933F934F935F936F937F938F939F9367
+:10498000AF93BF93EF93FF9380912A01813049F059
+:104990008091B905811105C00E94853481E00E9493
+:1049A0001C21FF91EF91BF91AF919F918F917F91CA
+:1049B0006F915F914F913F912F910F900FBE0F908C
+:1049C0001F90189593B190728091900186FD02C05E
+:1049D00080E298279923A9F020E030E080E09072EF
+:1049E00041E050E0992339F0BA01022E01C0660F70
+:1049F0000A94EAF7862B2F5F3F4F2430310591F759
+:104A0000089580E0089525982D9A8091900183FF64
+:104A100009C080916B00806280936B008091680078
+:104A2000816008C080916B008F7D80936B008091C6
+:104A300068008E7F8093680008956F927F928F92B6
+:104A40009F92AF92BF92CF92DF92EF92FF920F931D
+:104A50001F93CF93DF93EB018C015B0180E1A80EE4
+:104A6000B11C89E9682E96E7792E26E9822E3EE769
+:104A7000932EC990D990E990F99020E030E0A901F7
+:104A8000C701B6010E94D5368823B9F0A701960167
+:104A9000F80160817181828193810E94DF366B0110
+:104AA0007C01E894F7F89301A401C701B6010E94C4
+:104AB000F738181614F0360147010C5F1F4FCA155E
+:104AC000DB05B9F6B301C401DF91CF911F910F91BE
+:104AD000FF90EF90DF90CF90BF90AF909F908F901E
+:104AE0007F906F9008954F925F926F927F92AF92F6
+:104AF000BF92CF92DF92EF92FF920F931F93CF93CB
+:104B0000DF93EC015C0180E1A80EB11C8E01C12C89
+:104B1000D12C7601F80141905190619071908F01F4
+:104B200020E030E0A901C301B2010E94D5368823FC
+:104B300071F0A3019201C301B2010E9437399B01B8
+:104B4000AC01C701B6010E94E0356B017C010A157A
+:104B50001B0501F7C701B6010E94D6396B017C0124
+:104B60009B01AC0160E070E080E89FE30E94DF36CB
+:104B70002B013C01A3019201688179818A819B818B
+:104B80000E9437396993799389939993AC16BD063E
+:104B900089F7C701B601DF91CF911F910F91FF9067
+:104BA000EF90DF90CF90BF90AF907F906F905F902D
+:104BB0004F9008951F93CF93DF93142F20E030E0A0
+:104BC00040EA51E40E9437390E94BE360E945837AD
+:104BD000EB012097D1F080912B01811116C01111AA
+:104BE00003C00E94123306C00E94B11C80912C01A8
+:104BF00081FD0BC02FEF80E792E021508040904074
+:104C0000E1F700C000002197E4CFDF91CF911F9121
+:104C10000895EAE1F1E0A4E9B5E041915191619193
+:104C200071914D935D936D937D9381E0EA32F80726
+:104C3000A1F70895BF92CF92DF92EF92FF920F9368
+:104C40001F93CF93DF937C01FC0106A517A5D0A984
+:104C5000C1A9858980FF18C06091320170E080E0B1
+:104C600090E00E9489372AE037ED43E25CE30E943E
+:104C700037399B01AC01B8018D2F9C2F0E94373929
+:104C80008B01D82FC92F2DC082FD17C06091310133
+:104C900070E080E090E00E9489372AE037ED43E23F
+:104CA0005CE30E9437399B01AC01B8018D2F9C2F2A
+:104CB0000E9437398B01D82FC92FF701B2A4C3A4A2
+:104CC000D4A4F5A42B2D3C2D4D2D5F2DB8018D2F97
+:104CD0009C2F0E94F738181624F40B2D1C2DDD2D67
+:104CE000CF2D20E030E040E85FE3B8018D2F9C2F0E
+:104CF0000E94F738181624F000E010E0D0E8CFE367
+:104D0000B8018D2F9C2FDF91CF911F910F91FF90B4
+:104D1000EF90DF90CF90BF90089580919205909191
+:104D20009305981739F026E3829FC00111248E5C09
+:104D30009D4F089580E090E0089584E2E4E9F5E075
+:104D4000DF011D928A95E9F7109292051092930562
+:104D500081E0809331021092300208952F923F92A9
+:104D60004F925F926F927F928F929F92AF92BF927B
+:104D7000CF92DF92EF92FF920F931F93CF93DF9327
+:104D800030909305311002C0D0E101C0D32DD15035
+:104D9000C0913002CD1709F416C1ED2EF12C26E397
+:104DA000D29F800111240E5C1D4FF801A28CB38CA0
+:104DB000C48CD58C668D778D80A191A19B01AC01AF
+:104DC0000E94E035F80122A133A144A155A10E941F
+:104DD00037394B018C019B01AC01B501C6010E9422
+:104DE000D53687FD02C05401680186E38E9DF0012F
+:104DF0008F9DF00D1124EE5CFD4FC501D601868B11
+:104E0000978BA08FB18FD11101C0D0E1D150CD13BC
+:104E100007C080919205C81368C00E94223165C006
+:104E200026E32E9DC0012F9D900D11249C012E5C28
+:104E30003D4F790196E3292EAD2EB12C2A9CC0015D
+:104E40002B9C900D1124FC01EE5CFD4F6F01D111E4
+:104E500001C0D0E1D150809192058D1302C00E9413
+:104E600022312A9C80012B9C100D11240E5C1D4FB9
+:104E7000F801828C938CA48CB58CA5019401668971
+:104E80007789808D918D0E94D536882359F1F8015C
+:104E9000668D778D80A191A19B01AC010E94E035C8
+:104EA000F80122A133A144A155A10E943739F7018D
+:104EB00026893789408D518D0E94E0352B013C01B8
+:104EC0009B01AC01C501B4010E94F738F801181626
+:104ED0002CF4468A578A608E718E04C0868A978A1F
+:104EE000A08EB18E7601CD13A7CFF6E3CF9F8001C0
+:104EF00011240E5C1D4FCF5FC03109F4C0E0D0918A
+:104F0000300286E3282EC31509F45BC0CC2ED12CC9
+:104F10002C9CC0012D9C900D11249C012E5C3D4FBA
+:104F20007901F80146885788608C718CF901868870
+:104F30009788A08CB18CA5019401C301B2010E9495
+:104F4000D53687FF24C0F801668D778D80A191A1A9
+:104F50009B01AC010E94E035F80122A133A144A1DC
+:104F600055A10E943739A30192010E94E0352B011F
+:104F70003C019B01AC01C501B4010E94F738181631
+:104F800034F4F701468A578A608E718EDC2F2C9C90
+:104F9000F0012D9CF00D1124EE5CFD4F228D338D20
+:104FA000448D558D66897789808D918D0E94D53687
+:104FB000811101C0DC2FCF5FC03109F4C0E087014F
+:104FC000A2CFD0933002DF91CF911F910F91FF902C
+:104FD000EF90DF90CF90BF90AF909F908F907F9099
+:104FE0006F905F904F903F902F9008959F92AF9257
+:104FF000BF92CF92DF92EF92FF920F931F93CF93C6
+:10500000DF935B018A017C01E40EF51EEC01912C1B
+:105010006B01C81AD90AC6018C0F9D1FCE15DF057A
+:1050200089F00E946829911008C0292D990C330B32
+:10503000232F221F2227221F01C021E0982E920E2B
+:105040008993E9CFC501800F911F0E94682921E053
+:1050500030E0981611F020E030E0C901DF91CF91E7
+:105060001F910F91FF90EF90DF90CF90BF90AF9086
+:105070009F9008959F92AF92BF92CF92DF92EF924E
+:10508000FF920F931F93CF93DF935C018A017B0103
+:10509000E40EF51EEB01912C6C01C61AD70ACE0165
+:1050A0008C0D9D1DCE15DF0589F0911008C0292DAE
+:1050B000990C330B232F221F2227221F01C021E02E
+:1050C0006991922E960E0E944629E9CF692DC5015D
+:1050D000800F911FDF91CF911F910F91FF90EF9063
+:1050E000DF90CF90BF90AF909F900C944629CF93C4
+:1050F000DF9321E5829FC0011124EC01DD5F40E5D3
+:1051000050E0BE0189EA97E00E94F627892B69F4F6
+:105110001092A9070E94243440E550E069EA77E044
+:10512000CE010E943A2880E001C081E0DF91CF915A
+:1051300008951F93CF93DF93C0E9D1E01881812FA9
+:105140008F7D88836AE080E090E00E94462945E6F2
+:1051500050E06BE371E081E090E00E943A28188310
+:10516000DF91CF911F9108950F931F93CF93C82F75
+:105170008B010E94243421E1C29FC001112440E12F
+:1051800050E0B8019E5FCF911F910F910C943A2887
+:105190001F93CF93DF93182FEB0181E1189FB0018C
+:1051A00011247E5F40E150E0CE010E94F627892B5A
+:1051B00059F480E1FE0111928A95E9F7BE01812F31
+:1051C0000E94B42880E001C081E0DF91CF911F915F
+:1051D0000895EF92FF920F931F93CF93DF93CDB774
+:1051E000DEB760970FB6F894DEBF0FBECDBF182FA5
+:1051F00080FF0BC085E6E9E9F3E0ABE3B1E00590A1
+:105200000D928A95E1F70E94992811FF10C0CE01F6
+:1052100001967C0180E1F70111928A95E9F700E09F
+:10522000B701802F0E94B4280F5F0830C9F712FF22
+:1052300014C060E080E093E00E94462960E081E0D5
+:1052400093E00E94462960E081E593E00E944629B0
+:1052500060E082E593E00E94462913FF0AC060E007
+:105260008EEA93E00E94462960E08FEA93E00E9474
+:10527000462960960FB6F894DEBF0FBECDBFDF9112
+:10528000CF911F910F91FF90EF900895F894F999A5
+:10529000FECF92BD81BD81E08FBB80B58627482FB0
+:1052A000082E000C550B262F30E024233523232B0A
+:1052B00039F06F3F19F060BD84E006C084E104C09E
+:1052C000882321F060BD84E28FBBF99A7894089519
+:1052D000F999FECF92BD81BD81E08FBB80B5089565
+:1052E0002F923F924F925F926F927F928F929F92F6
+:1052F000AF92BF92CF92DF92EF92FF920F931F93E4
+:10530000CF93DF93CDB7DEB7EC970FB6F894DEBF3F
+:105310000FBECDBF80912E0180FD7BC79091EA0525
+:105320008091B406981709F474C78091B7069091DC
+:10533000B806892B09F00BC310912E0112FF09C08A
+:105340008091930526E3289FC00111248E5C9D4F18
+:1053500002C00E948D269093B8068093B7062090D5
+:10536000B7063090B8062114310409F452C70091F1
+:105370008506802F817000FF03C010928506EAC069
+:10538000909184069F5F953009F0892F8093840661
+:10539000E82EF12C36E13E9DF0013F9DF00D1124E9
+:1053A000EA5EF94FF093B606E093B506D101549644
+:1053B0009C915497948BE6E18E9FC001112420E0CC
+:1053C00030E04D915D916D917D91F901E80FF91FEC
+:1053D000EA5EF94F689412F8440F551F661F771F55
+:1053E0001694D1F740835183628373832C5F3F4FC0
+:1053F0002031310531F726E12E9DF0012F9DF00D72
+:1054000011249F012A5E394FF10180899189A28977
+:10541000B38943E0880F991FAA1FBB1F4A95D1F794
+:10542000F901808B918BA28BB38BD10150966D913A
+:105430007D918D919C9153970E94873760938A0646
+:1054400070938B0680938C0690938D06F10122A1B8
+:1054500033A144A155A10E94DF369B01AC016093AA
+:105460008E0670938F06809390069093910660E06D
+:1054700070E080EA9FE30E94DF36609392067093AB
+:105480009306809394069093950610928606109248
+:105490008706109288061092890611FD02C003FF4C
+:1054A0001FC06091A3067091A4068091A5069091FB
+:1054B000A60660939B0670939C0680939D0690932E
+:1054C0009E069B01AC010E943739D10156966D931F
+:1054D0007D938D939C935997077F009385060FC00A
+:1054E000F10166897789808D918D0E94D63960930C
+:1054F0009B0670939C0680939D0690939E06F6E112
+:10550000FE9DC001FF9D900D11249C012A5E394F24
+:105510007901D90155961C928091900181FF1AC0A2
+:10552000F101858985FF16C026A537A540A951A997
+:1055300060E070E080E89FE30E94DF366093AF0692
+:105540007093B0068093B1069093B206E1E0D70164
+:105550005596EC9310929706109298061092990621
+:1055600010929A06D1015E962D913D914D915C91DC
+:10557000919729833A834B835C8360E070E080E0FD
+:105580009FE30E94DF366D837E838F839887F101CE
+:1055900042A053A064A075A086889788A08CB18C87
+:1055A00011FF44C0F2E0F0939606A50194010E9419
+:1055B00037399B01AC01C301B2010E94DF356B0199
+:1055C0007C0120E030E0A9010E94D53687FF1DC094
+:1055D00029813A814B815C81CA01B9010E94E03581
+:1055E000A30192010E9437399B01AC01C501B401AE
+:1055F0000E94DF350E94D6396093A3067093A406FB
+:105600008093A5069093A6069FC1C0929706D0925C
+:105610009806E0929906F0929A061092A3061092CC
+:10562000A4061092A5061092A6068EC110929606A8
+:105630004092A7065092A8066092A9067092AA0608
+:1056400012FF0CC01092A3061092A4061092A50699
+:105650001092A606C12CD12C760124C0E0919205AF
+:10566000EF5FE03109F4E0E0809193058E1759F087
+:1056700026E32E9FF0011124EE5CFD4FC688D788EB
+:10568000E08CF18C03C0C12CD12C7601C701B6018E
+:105690000E94D6396093A3067093A4068093A50652
+:1056A0009093A606C1010E941A26698B7A8B8B8B78
+:1056B0009C8B9B01AC010E94373969877A878B8765
+:1056C0009C87A50194010E94D53687FF6CC029856F
+:1056D0003A854B855C85C501B4010E94DF352D817B
+:1056E0003E814F8158850E9437399B01AC01C3012F
+:1056F000B2010E94DF356093A7067093A8068093DD
+:10570000A9069093AA0620E030E0A9010E94D536B0
+:1057100018162CF132E03093960629813A814B819C
+:105720005C81CA01B9010E94E035A30192010E9487
+:1057300037399B01AC01C501B4010E94DF350E94DD
+:10574000D6396093A3067093A4068093A506909320
+:10575000A60680918506886080938506F5C0A7011E
+:10576000960169857A858B859C850E94DF352D8120
+:105770003E814F8158850E9437396093AB06709304
+:10578000AC068093AD069093AE0689899A89AB8961
+:10579000BC8980939F069093A006A093A106B09326
+:1057A000A20693E0B6C0A7019601C501B4010E940C
+:1057B000DF352D813E814F8158850E943739A30105
+:1057C00092010E94E03520E030E040E05FE30E947B
+:1057D00037396D877E878F87988B20E030E0A9016D
+:1057E0000E94F73818160CF097C02D853E854F851E
+:1057F0005889C301B2010E94F73818160CF088C00E
+:10580000A701960169857A858B859C850E94DF3585
+:105810002D813E814F8158850E9437399B01AC0113
+:105820006093AB067093AC068093AD069093AE0682
+:105830006D857E858F8598890E94F7381816BCF58E
+:1058400029893A894B895C8920939F063093A00669
+:105850004093A1065093A206A501940169857A851B
+:105860008B859C850E94D536811104C031E0309330
+:1058700096066AC0A501940169857A858B859C8509
+:105880000E94DF352D813E814F8158850E94373936
+:105890009B01AC01C301B2010E94DF356093A706F2
+:1058A0007093A8068093A9069093AA064DC08D8593
+:1058B0009E85AF85B8898093A7069093A806A0938C
+:1058C000A906B093AA068093AB069093AC06A0936A
+:1058D000AD06B093AE0629813A814B815C81CA0145
+:1058E000B9010E94E0352D853E854F8558890E947B
+:1058F0003739A70196010E94E0350E94D63960939E
+:105900009F067093A0068093A1069093A2061CC0E8
+:1059100092E09093960618C01092A7061092A806DF
+:105920001092A9061092AA068091A3069091A4064F
+:10593000A091A506B091A60680939F069093A0061D
+:10594000A093A106B093A206186010932E01809137
+:105950001506A82FB0E0BA8BA98B27E02A9FF0018B
+:105960002B9FF00D1124E551FA4F8091840684831A
+:105970004091B7065091B8065CAF4BAFDA019296F2
+:10598000BC91BAA3FA01F3A1FBA3DA019496BC91EE
+:10599000BCA3FA01F5A1FDA32091920630919306D4
+:1059A00040919406509195066AA17BA18B2F9F2F61
+:1059B0000E94DF356EA77FA788AB99AB20E030E06F
+:1059C000A9010E94D53687FF04C01EA61FA618AAEB
+:1059D00019AA20919F062E8B3091A0063F8B4091F3
+:1059E000A106488F5091A206598F8091A7068A8FF1
+:1059F0009091A8069B8FA091A906AC8FB091AA06A2
+:105A0000BD8FE0919B06E983F0919C06FD83209178
+:105A10009D06298730919E063D874091AB064EA397
+:105A20005091AC065FA38091AD0688A79091AE0619
+:105A300099A7A0919606AD8BB0919706BAA7E09171
+:105A40009806EBA7F0919906FCA720919A062DA73E
+:105A50003091A3063BAB4091A4064CAB5091A506F8
+:105A60005DAB8091A6068EAB2EA13FA148A5592F14
+:105A70006A8D7B8D8C8D9D8D0E94D536811103C0E2
+:105A800092E09AAB02C0A1E0AAAB3AA02BA01CA165
+:105A90000DA18EE3C82E83ECD82E8EE2E82E89E38A
+:105AA000F82E412C512C3201CE8EDF8EE8A2F9A2C5
+:105AB0003D89313009F4F2C0ABADBCAD313008F4F2
+:105AC0007BC0333009F020C15E962D913D914D9100
+:105AD0005C919197C701B6010E9437394B015C0177
+:105AE0002E893F89488D598D69817D8189859D8564
+:105AF0000E94DF359B01AC01C501B4010E94F7385B
+:105B000087FD31C02A8D3B8D4C8D5D8D6AA17BA1B7
+:105B10008CA19DA10E94DF359B01AC010E94E03564
+:105B20006B017C0129813D8149855D856E897F8975
+:105B3000888D998D0E94E0359B01AC01C701B601AB
+:105B40000E94DF366B017C01BE89B983EF89ED834A
+:105B5000F88DF987298D2D873A8C2B8C1C8D0D8D16
+:105B600031E03D8B51C120E030E040E05FE3C50112
+:105B7000B4010E9437399B01AC0169817D8189851F
+:105B80009D850E94DF35A70196010E9437399B0150
+:105B9000AC01632D722D812F902F0E94DF35362EA0
+:105BA000272E182F092FA501940169817D818985F0
+:105BB0009D850E94DF356DC05E962D913D914D9182
+:105BC0005C919197C701B6010E9437394B015C0186
+:105BD00020E030E040E05FE30E94373929813D81D9
+:105BE00049855D850E94E035A70196010E943739FD
+:105BF0009B01AC01632D722D812F902F0E94DF3508
+:105C0000362E272E182F092F262F372F482F592FA2
+:105C10006A8D7B8D8C8D9D8D0E94F73818168CF5C2
+:105C20002A8D3B8D4C8D5D8D6AA17BA18CA19DA1A0
+:105C30000E94DF359B01AC010E94E0356B017C01C5
+:105C400029813D8149855D856E897F89888D998D02
+:105C50000E94E0359B01AC01C701B6010E94DF360E
+:105C60006B017C01BAA9BD8BEE89E983FF89FD83B5
+:105C7000288D2987398D3D873A8C2B8C1C8D0D8D75
+:105C8000C3C0A501940169817D8189859D850E949C
+:105C9000E03569837D8389879D87B6C02E893F89DA
+:105CA000488D598DC701B6010E9437399B01AC015F
+:105CB000632D722D812F902F0E94DF355B014C01E7
+:105CC0009B01AC016EA17FA188A599A50E94F73820
+:105CD00018160CF095C02EA13FA148A559A5632D1B
+:105CE000722D812F902F0E94DF352E893F89488D9C
+:105CF000598D0E94DF366B017C013EA02FA018A5B4
+:105D000009A542E04D8B80C05E962D913D914D914D
+:105D10005C919197C701B6010E9437394B015C0134
+:105D200029813D8149855D850E94D53687FF39C02F
+:105D300020E030E040E05FE3C501B4010E94373964
+:105D40009B01AC0169817D8189859D850E94DF353C
+:105D5000A70196010E9437399B01AC01632D722D7A
+:105D6000812F902F0E94DF356FAB78AF89AF9AAF4C
+:105D70002AA53BA54CA55DA50E94F73818168CF402
+:105D8000A501940169817D8189859D850E94DF350A
+:105D900069837D8389879D873FA828AC19AD0AADAB
+:105DA00033C02AA53BA54CA55DA5632D722D812F7F
+:105DB000902F0E94DF359B01AC010E94E0356B0102
+:105DC0007C0129813D8149855D856BA97CA98DA9CF
+:105DD0009EA90E94E0359B01AC01C701B6010E945B
+:105DE000DF366B017C01BBA9B983ECA9ED83FDA96A
+:105DF000F9872EA92D873AA42BA41CA50DA504C0B4
+:105E00003A2C2B2C182D092DA7019601C301B201A4
+:105E10000E94E0352B013C012E8D3F8D48A159A1F8
+:105E20000E94D53687FF07C0A30192016E8D7F8D3A
+:105E300088A199A11CC02EA53FA548A959A9632DE9
+:105E4000722D812F902F0E94F738181684F52EE3BB
+:105E500033EC4EE259E36E8D7F8D88A199A10E94AB
+:105E6000E0356E8F7F8F88A399A3A30192010E94D2
+:105E7000DF356B017C01232D322D412F502F6AA578
+:105E80007BA58CA59DA50E94D53687FD11CE898165
+:105E90009D81A985BD8580939B0690939C06A093C8
+:105EA0009D06B0939E063D89309396060FC089816A
+:105EB0009D81A985BD8580939B0690939C06A093A8
+:105EC0009D06B0939E064D8940939606E091B506D7
+:105ED000F091B6069589911104C080912E0183FF3F
+:105EE0003FC0ABADBCAD55968C915597807349F1D1
+:105EF000D296CD90DD90ED90FC90D5979923D1F07E
+:105F00002091AF063091B0064091B1065091B20693
+:105F100060919B0670919C0680919D0690919E06D3
+:105F20000E9437399B01AC01C701B6010E94373985
+:105F30006B017C01C701B6010E9443328093B30616
+:105F40000AC0109237011092380110923901109254
+:105F50003A011092B30680912E01877F80932E0123
+:105F600047E029893A89429FF001439FF00D1124AF
+:105F7000E551FA4FFA87E9878091B306868320912D
+:105F80008E0630918F064091900650919106632DB8
+:105F9000722D812F902F0E94373969837A838B83EA
+:105FA0009C830E94BE366B017C0160918A067091D1
+:105FB0008B0680918C0690918D060E94BE364B0117
+:105FC0005C01A70196010E94DF350E9458376D835E
+:105FD0007E838F839887ED81FE81A985BA85ED93B5
+:105FE000FC93EF2B41F480912E0181FF04C081606E
+:105FF00080932E010EC12091860630918706409134
+:10600000880650918906C301B2010E94E0352B0138
+:106010003C0129813A814B815C81C501B4010E9418
+:10602000DF359B01AC01C301B2010E94DF364B0199
+:106030005C0120EC31EE44E65EE40E9437390E94B8
+:10604000BE360E945837603D37E0730781059105E1
+:1060500060F4A7E049895A89A49FF001A59FF00D3B
+:106060001124E551FA4F158253C0603ABFE07B0717
+:106070008105910560F447E029893A89429FF00142
+:10608000439FF00D1124E551FA4F51E011C06034E7
+:10609000AFE17A078105910568F447E029893A89DB
+:1060A000429FF001439FF00D1124E551FA4F52E059
+:1060B00055830CC027E0A989BA892A9FF0012B9F3C
+:1060C000F00D1124E551FA4F33E03583A7E04989FB
+:1060D0005A89A49FF001A59FF00D1124E551FA4FB4
+:1060E0002581022E04C096958795779567950A9429
+:1060F000D2F74D815E8102C0440F551F2A95E2F709
+:10610000518340836115710551E08507910568F45D
+:1061100027E0A989BA892A9FF0012B9FF00D11244D
+:10612000E551FA4F7383628304C04FEF5FEF5383EF
+:1061300042838091B406809315068F5F863019F0F4
+:106140008093B40602C01092B406E091B706F091B5
+:10615000B806832D922DA12FB02F82A393A3A4A3C1
+:10616000B5A3C0928A06D0928B06E0928C06F0927C
+:106170008D0629813A814B815C81C701B6010E945D
+:10618000DF35A50194010E9437396093860670932C
+:106190008706809388069093890620919706309110
+:1061A00098064091990650919A06632D722D812F81
+:1061B000902F0E94D5368111B1C820E030E0A901AE
+:1061C000632D722D812F902F0E94F73890912E0110
+:1061D000181614F092FF04C0916090932E0119C01C
+:1061E0001092B8061092B70690919205809193058F
+:1061F000891709F493C881E0890F803109F480E0A0
+:1062000020913002921302C0809330028093920555
+:1062100085C8EC960FB6F894DEBF0FBECDBFDF91F8
+:10622000CF911F910F91FF90EF90DF90CF90BF9093
+:10623000AF909F908F907F906F905F904F903F9026
+:106240002F900895CF93DF93C091B706D091B806F1
+:106250002097C9F0809185068160809385066091C2
+:106260009B0670919C0680919D0690919E069B01D5
+:10627000AC010E9437396E8B7F8B888F998F10927B
+:10628000B8061092B706DF91CF91089560917C0116
+:1062900070E0E0917D01F0E080E090E040E050E0CF
+:1062A000282FDB01082E02C0B595A7950A94E2F7C6
+:1062B000A0FF0EC0882331F0813031F0823031F000
+:1062C00030E405C034E003C038E001C030E2532BB5
+:1062D000DF0102C0B595A7952A95E2F7A0FF0EC091
+:1062E000882331F0813031F0823031F020E105C077
+:1062F00021E003C022E001C024E0422B019685305A
+:10630000910571F65093BC054093BD050895CF9358
+:10631000DF930E94AE1980E3E4E8F6E0DF011D920E
+:106320008A95E9F7CFEBD5E08BE2FE0111928A95D1
+:10633000E9F71092B8061092B7061092EA0510928B
+:10634000150681E08093B4061092BE050E94463186
+:106350008091BD058B8B8BB19091BC059C77837828
+:10636000892B8BB985B19091BD059771887E892B5A
+:1063700085B9DF91CF9108951F920F920FB60F92BA
+:1063800011248F939F938BB19091BC059C77837858
+:10639000892B8BB915BC9F918F910F900FBE0F90D9
+:1063A0001F901895E0EBF0E080818F778083439811
+:1063B00008950E948D26892B19F082E00C942C21DF
+:1063C00008951F920F920FB60F9211248F939F93EF
+:1063D000EF93FF938091A807E82FF0E0E15CF84F7E
+:1063E00090819093C6008F5F893609F480E0809396
+:1063F000A80790913E07891305C08091C1008F7D49
+:106400008093C100FF91EF919F918F910F900FBEEC
+:106410000F901F901895E0913E0791E09E0F9936DE
+:1064200031F490E004C02091B80524FD0FC0209104
+:10643000A8072917C1F3F0E0E15CF84F808390933F
+:106440003E078091C10080628093C10008959091C1
+:106450002B0191110CC086FF02C0449A01C04498E0
+:1064600087FF02C0459A01C04598109235010895F2
+:10647000449845980895449B02C080E401C080E0A0
+:106480004599806808954F925F926F927F928F92A4
+:106490009F92AF92BF92CF92DF92EF92FF926B01E9
+:1064A0007C0180908C0190908D01A0908E01B09025
+:1064B0008F01409088015090890160908A0170900E
+:1064C0008B01A3019201C501B4010E94F73887FF37
+:1064D00019C06091330170E080E090E00E9489373C
+:1064E0002AE037ED43E25CE30E943739A7019601C9
+:1064F0000E9437396B017C01A30192010E94F73899
+:1065000087FD0AC0409237015092380160923901EC
+:1065100070923A018FEF47C0A5019401C701B601FF
+:106520000E94D5361816E4F020E030E0A901C7013A
+:10653000B6010E94D536811109C010923701109220
+:1065400038011092390110923A012DC08092370122
+:1065500090923801A0923901B0923A0181E023C0B3
+:10656000C0923701D0923801E0923901F0923A019D
+:10657000A5019401C701B6010E94DF3520913A07B9
+:1065800030913B0740913C0750913D070E943739BD
+:106590000E94C43720E030E040E85FE30E94E0352D
+:1065A0000E945837862FFF90EF90DF90CF90BF90DA
+:1065B000AF909F908F907F906F905F904F900895D5
+:1065C0008093B300811104C08091B0008F7703C025
+:1065D0008091B00080688093B000089590912B0165
+:1065E00091111FC081110BC0109237011092380118
+:1065F0001092390110923A010E94D23110C090914C
+:10660000900191FF05C0803219F440E050E0BA01DA
+:10661000CB01BA010E9443320E94E032439A1092A9
+:1066200035010895CF92DF92EF92FF921F93CF939F
+:10663000DF930E94B11C80912C01882309F4FCC0D7
+:106640000E948D267C01009771F4C091A901809170
+:10665000A801C82BC090AB01D090AC01E090AD0177
+:10666000F090AE010BC0FC01C589C0730E943B32A3
+:10667000C82BF701C2A8D3A8E4A8F5A8809190017F
+:1066800081FF03C080E20E940A21DC2FD07311E059
+:10669000C07C80912C01882309F4CEC090912B01FD
+:1066A0009111CAC080FFC5C090912A01292F207C7A
+:1066B00009F474C082FD12C01092340140E050E031
+:1066C000BA0180E00E94EE3280E00E942732809181
+:1066D0002C018D7F846080932C01ABC09038E1F455
+:1066E0008BEA92E00E94172384E192E00E94172334
+:1066F0000E94842440E050E0BA0180E00E94EE3223
+:1067000080E00E9427320E94AE1980912B018111F6
+:1067100093C00E94B11CF9CF903419F48F7D8093FF
+:106720002C0180912C0183FF84C09091A9019923B1
+:10673000C1F081FD16C08091900181FF06C080915B
+:106740002E01886080932E010CC0B701A6018D2F09
+:106750000E94EE3241E060E070E080E890E40E9448
+:10676000DA258091A801882371F080912C0181FDA8
+:106770000AC08C2F0E94273241E060E070E080E880
+:106780009FE30E94DA2580912C0181FD52C0806137
+:1067900080932C0182E00E942C214BC08091340117
+:1067A0008823C9F181FF0DC08091A9018823B1F030
+:1067B00040E050E0BA0180E00E94EE3210933401D4
+:1067C00038C08C70B1F18091A90181110AC080910B
+:1067D000340183FF03C082E00E942C211092340117
+:1067E00028C08BEA92E00E9417238DE192E00E947C
+:1067F00017230E9484248091900181FF06C080911C
+:106800002E01886080932E01E2CFB701A6018D2F63
+:106810000E94EE32DCCF80912E0183FF0AC0B701C7
+:10682000A6018D2F0E94EE3280912E01877F8093EA
+:106830002E010E94B11C2DCFDF91CF911F91FF90AF
+:10684000EF90DF90CF9008950E94D9310E941233CB
+:1068500080912B01811108C00E948D26892BB1F7F0
+:1068600080912A01883091F30895CF92DF92EF92C0
+:10687000FF92CF9390912A01923079F06A017B01C7
+:10688000C82F0E942434B701A6018C2FCF91FF900E
+:10689000EF90DF90CF900C94EE32CF91FF90EF907D
+:1068A000DF90CF900895239A83E08093B00087E033
+:1068B0008093B1003B9A20918C0130918D014091E1
+:1068C0008E0150918F0160918801709189018091B2
+:1068D0008A0190918B010E94DF359B01AC0160E041
+:1068E00070E08EE793E40E94DF3660933A0770937E
+:1068F0003B0780933C0790933D070C94D2316AE1AB
+:1069000071E088EB91E00C9470218091B80584FDD2
+:106910001DC080E10E942C210E94D2310E94383299
+:1069200090912A01892F8C7221F480912E0186701A
+:1069300069F0943031F48091B905811105C086E089
+:1069400001C083E00E941C210C94AE1908951F928F
+:106950000F920FB60F9211242F933F934F935F9393
+:106960006F937F938F939F93AF93BF93EF93FF9317
+:10697000E091C600E13271F028F4E83179F40E9428
+:10698000853458C0EF3321F0EE3741F482E003C084
+:1069900081E001C088E00E942C214CC0E7FF39C093
+:1069A0008E2F90E0FC01E458F109EE31F10508F07A
+:1069B00041C0E359FF4F0C94823A80E2ECCF8091C2
+:1069C0002A0185FF37C080E4E6CF81E00DC082E078
+:1069D0000BC084E009C088E007C080E105C080E208
+:1069E00003C080E401C080E80E94132123C081E03D
+:1069F0000DC082E00BC084E009C088E007C080E1E0
+:106A000005C080E203C080E401C080E80E940A2142
+:106A100011C0A0912E0281E08A0F813809F480E034
+:106A200090912F02891731F0B0E0A754B94FEC9341
+:106A300080932E02FF91EF91BF91AF919F918F9123
+:106A40007F916F915F914F913F912F910F900FBE6A
+:106A50000F901F9018958F929F92AF92BF92CF92F6
+:106A6000DF92EF92FF920F931F93CF93DF937C01FE
+:106A70006B01DC01CC91D0E0FE01E755F84F80813D
+:106A80008D3229F4EF012296818101E009C08B3219
+:106A900019F0C655D84F03C0EF012296818100E05E
+:106AA000912CF0E010E020E030E0A9015E01E0ED83
+:106AB000E80FEA30D0F4FF5FF93098F491101150EC
+:106AC000A5E0B0E00E94883ADC01CB01880F991F55
+:106AD000AA1FBB1F9C01AD012E0F311D411D511D71
+:106AE0000AC0911008C01F5F06C0EE3F41F491102C
+:106AF00006C0992493942196D5018C91D7CFFF237A
+:106B000009F44FC0CA01B9010E9487374B015C01EB
+:106B100020E030E0A9010E94D536882349F11F3FCB
+:106B200064F42AE037ED43E25CE3C501B4010E945E
+:106B300037394B015C011E5FF2CF1F3F59F42DEC3A
+:106B40003CEC4CEC5DE3C501B4010E9437394B01CC
+:106B50005C010EC0112361F020E030E040E251E41E
+:106B6000C501B4010E9437394B015C011150F2CFCD
+:106B7000002351F0B7FAB094B7F8B094F6018082D0
+:106B80009182A282B38206C0D6018D929D92AD926F
+:106B9000BC921397C95AD740C150F701C08381E016
+:106BA00001C080E0DF91CF911F910F91FF90EF9096
+:106BB000DF90CF90BF90AF909F908F9008955058E6
+:106BC000BB27AA270E94F7350C9491380E945838A9
+:106BD00038F00E945F3820F039F49F3F19F426F412
+:106BE0000C942E380EF4E095E7FB0C94FF37E92F58
+:106BF0000E94B63858F3BA176207730784079507DF
+:106C000020F079F4A6F50C94F0380EF4E0950B2EF4
+:106C1000BA2FA02D0B01B90190010C01CA01A001EE
+:106C20001124FF27591B99F0593F50F4503E68F149
+:106C30001A16F040A22F232F342F4427585FF3CF8A
+:106C4000469537952795A795F0405395C9F77EF45B
+:106C50001F16BA0B620B730B840BBAF09150A1F0A4
+:106C6000FF0FBB1F661F771F881FC2F70EC0BA0F2A
+:106C7000621F731F841F48F4879577956795B795B2
+:106C8000F7959E3F08F0B0CF9395880F08F09927AD
+:106C9000EE0F9795879508950E94583860F080E828
+:106CA00091E009F49EEF0E945F3828F040E851E03F
+:106CB00071F45EEF0CC00C942E380C94F038E92F70
+:106CC000E0780E94B63840F3092E052AB1F3261762
+:106CD00037074807590738F00E2E07F8E02569F006
+:106CE000E025E0640AC0EF6307F8009407FADB01CF
+:106CF000B9019D01DC01CA01AD01EF930E94F63696
+:106D00000E9491380E948F365F91552339F02BED08
+:106D10003FE049E450FD49EC0C94E0350895DF93E1
+:106D2000DD27B92FBF7740E85FE31616170648073F
+:106D30005B0718F4D92F0E94FC389F938F937F93A1
+:106D40006F930E94163AE6E1F1E00E9431380E940A
+:106D500091382F913F914F915F910E944A39DD23E5
+:106D600051F09058A2EA2AED3FE049EC5FE3D07879
+:106D70005D270E94F735DF910C9491380E94D83836
+:106D800090F09F3748F4911116F40C94F13860E0BC
+:106D900070E080E89FE3089526F01B16611D711DC9
+:106DA000811D0C9405380C9420380E94DB3708F4C0
+:106DB00081E008950E946838E3950C94A2380E94FF
+:106DC000F3360C9491380E945F3858F00E9458387E
+:106DD00040F029F45F3F29F00C94FF3751110C94D7
+:106DE000F1380C942E380E94B63868F39923B1F329
+:106DF000552391F3951B550BBB27AA2762177307E1
+:106E0000840738F09F5F5F4F220F331F441FAA1F74
+:106E1000A9F335D00E2E3AF0E0E832D09150504030
+:106E2000E695001CCAF72BD0FE2F29D0660F771FDE
+:106E3000881FBB1F261737074807AB07B0E809F0C4
+:106E4000BB0B802DBF01FF2793585F4F3AF09E3F49
+:106E5000510578F00C94FF370C94F1385F3FE4F360
+:106E6000983ED4F3869577956795B795F7959F5F8C
+:106E7000C9F7880F911D9695879597F90895E1E0D8
+:106E8000660F771F881FBB1F621773078407BA0737
+:106E900020F0621B730B840BBA0BEE1F88F7E09592
+:106EA00008950E9458376894B1110C94F1380895F0
+:106EB0000E94BE3888F09F5798F0B92F9927B75194
+:106EC000B0F0E1F0660F771F881F991F1AF0BA958E
+:106ED000C9F714C0B13091F00E94F038B1E00895C4
+:106EE0000C94F038672F782F8827B85F39F0B93FB6
+:106EF000CCF3869577956795B395D9F73EF4909541
+:106F00008095709561957F4F8F4F9F4F0895E894BE
+:106F100009C097FB3EF490958095709561957F4FE1
+:106F20008F4F9F4F9923A9F0F92F96E9BB2793958F
+:106F3000F695879577956795B795F111F8CFFAF49F
+:106F4000BB0F11F460FF1BC06F5F7F4F8F4F9F4FD0
+:106F500016C0882311F096E911C0772321F09EE82E
+:106F6000872F762F05C0662371F096E8862F70E094
+:106F700060E02AF09A95660F771F881FDAF7880F6E
+:106F80009695879597F908950E94D83890F09F3785
+:106F900048F4911116F00C94F13860E070E080E84C
+:106FA0009FEB089526F41B16611D711D811D0C9425
+:106FB00005380C942038990F0008550FAA0BE0E80B
+:106FC000FEEF16161706E807F907C0F012161306AB
+:106FD000E407F50798F0621B730B840B950B39F4EB
+:106FE0000A2661F0232B242B252B21F408950A2651
+:106FF00009F4A140A6958FEF811D811D089597F991
+:107000009F6780E870E060E00895882371F477233B
+:1070100021F09850872B762F07C0662311F499270B
+:107020000DC09051862B70E060E02AF09A95660FB3
+:10703000771F881FDAF7880F9695879597F9089537
+:107040009F3F31F0915020F4879577956795B795DC
+:10705000880F911D9695879597F908959FEF80EC7D
+:107060000895DF93CF931F930F93FF92EF92DF92D8
+:107070007B018C01689406C0DA2EEF010E944A3928
+:10708000FE01E894A5912591359145915591A6F37E
+:10709000EF010E94F735FE019701A801DA9469F724
+:1070A000DF90EF90FF900F911F91CF91DF910895A6
+:1070B00000240A94161617061806090608950024D7
+:1070C0000A94121613061406050608950C942E3819
+:1070D0000E94BE38D8F3E894E0E0BB279F57F0F059
+:1070E0002AED3FE049EC06C0EE0FBB0F661F771F8D
+:1070F000881F28F0B23A62077307840728F0B25A53
+:10710000620B730B840BE3959A9572F7803830F419
+:107110009A95BB0F661F771F881FD2F790480C9473
+:107120002238092E0394000C11F4882352F0BB0F6F
+:1071300040F4BF2B11F460FF04C06F5F7F4F8F4F8F
+:107140009F4F0895EF93E0FF07C0A2EA2AED3FE0CA
+:1071500049EC5FEB0E94F7350E9491380F90039441
+:1071600001FC9058E3E4F1E00C942C3A57FD905860
+:10717000440F551F59F05F3F71F04795880F97FBFB
+:10718000991F61F09F3F79F08795089512161306B5
+:107190001406551FF2CF4695F1DF08C016161706E4
+:1071A0001806991FF1CF8695710561050894089519
+:1071B0000E94BE38A0F0BEE7B91788F4BB279F38FD
+:1071C00060F41616B11D672F782F8827985FF7CFC8
+:1071D000869577956795B11D93959639C8F308956F
+:1071E000E894BB2766277727CB0197F908950E947B
+:1071F000DB3708F48FEF08959B01AC0160E070E08D
+:1072000080E89FE30C94DF360E94BE3858F19E5709
+:1072100060F19851A0F0E9F0983020F5092E9927F7
+:10722000660F771F881F991F0A94D1F712C0062E88
+:10723000672F782F8827985F11F4000C07C0993FBB
+:10724000B4F38695779567959395D9F7611D711D70
+:10725000811D3EF490958095709561957F4F8F4F7D
+:107260009F4F089568940C94F1380C94F0380E9464
+:107270004A390C9491380E94583838F00E945F388F
+:1072800020F0952311F00C94FF370C942E38112424
+:107290000C94F1380E94B63870F3959FC1F3950FA6
+:1072A00050E0551F629FF001729FBB27F00DB11D8A
+:1072B000639FAA27F00DB11DAA1F649F6627B00D1A
+:1072C000A11D661F829F2227B00DA11D621F739F03
+:1072D000B00DA11D621F839FA00D611D221F749F11
+:1072E0003327A00D611D231F849F600D211D822F58
+:1072F000762F6A2F11249F5750409AF0F1F088237F
+:107300004AF0EE0FFF1FBB1F661F771F881F9150AB
+:107310005040A9F79E3F510580F00C94FF370C9424
+:10732000F1385F3FE4F3983ED4F3869577956795FF
+:10733000B795F795E7959F5FC1F7FE2B880F911DD5
+:107340009695879597F908950E94BE38E8F09E3784
+:10735000E8F09639B8F49E3848F4672F782F8827DC
+:10736000985FF9CF86957795679593959539D0F382
+:10737000B62FB1706B0F711D811D20F48795779525
+:10738000679593950C9405380C9420380C94F1383B
+:107390009F930E9468380F9007FCEE5F0C94A23810
+:1073A00019F416F40C942E380C9420380E94BE3830
+:1073B000B8F39923C9F3B6F39F57550B87FF0E9483
+:1073C000253A0024A0E640EA90018058569597950A
+:1073D00028F4805C660F771F881F20F02617370778
+:1073E000480730F4621B730B840B202931294A2B88
+:1073F000A69517940794202531254A2758F7660F3C
+:10740000771F881F20F026173707480730F4620BD4
+:10741000730B840B200D311D411DA09581F7B9011F
+:10742000842F9158880F9695879508959B01AC01FC
+:107430000C9437390E94D83830F09F3710F40C94F0
+:10744000F1380C9405380C94203891505040660F58
+:10745000771F881FD2F708959F938F937F936F9321
+:10746000FF93EF939B01AC010E943739EF91FF919D
+:107470000E9431382F913F914F915F910C94373991
+:10748000991B79E004C0991F961708F0961B881F76
+:107490007A95C9F780950895AA1BBB1B51E107C0D7
+:1074A000AA1FBB1FA617B70710F0A61BB70B881F94
+:1074B000991F5A95A9F780959095BC01CD01089523
+:1074C000A1E21A2EAA1BBB1BFD010DC0AA1FBB1FE8
+:1074D000EE1FFF1FA217B307E407F50720F0A21B5A
+:1074E000B30BE40BF50B661F771F881F991F1A94C7
+:1074F00069F760957095809590959B01AC01BD01F1
+:10750000CF010895EE0FFF1F0590F491E02D09942F
+:107510000E94933AA59F900DB49F900DA49F800D5B
+:10752000911D11240895A29FB001B39FC001A39F94
+:10753000700D811D1124911DB29F700D811D1124AC
+:10754000911D0895FB01DC0104C08D91019080190B
+:1075500021F441505040C8F7881B990B0895F894C6
+:02756000FFCF5B
+:0875620058595A434241000050
+:00000001FF
diff --git a/img/arduino.png b/img/arduino.png
new file mode 100644
index 000000000..16bd19478
Binary files /dev/null and b/img/arduino.png differ
diff --git a/img/arduino_wiring.jpg b/img/arduino_wiring.jpg
new file mode 100644
index 000000000..5716c2f29
Binary files /dev/null and b/img/arduino_wiring.jpg differ
diff --git a/jog.c b/jog.c
new file mode 100644
index 000000000..553af77e4
--- /dev/null
+++ b/jog.c
@@ -0,0 +1,50 @@
+/*
+ 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 .
+*/
+
+#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(plan_line_data_t *pl_data, parser_block_t *gc_block)
+{
+ // Initialize planner data struct for jogging motions.
+ // NOTE: Spindle and coolant are allowed to fully function with overrides during a jog.
+ pl_data->feed_rate = gc_block->values.f;
+ pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE;
+ #ifdef USE_LINE_NUMBERS
+ pl_data->line_number = gc_block->values.n;
+ #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);
+}
diff --git a/jog.h b/jog.h
new file mode 100644
index 000000000..c72624634
--- /dev/null
+++ b/jog.h
@@ -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 .
+*/
+
+#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(plan_line_data_t *pl_data, parser_block_t *gc_block);
+
+#endif
diff --git a/limits.c b/limits.c
index 12cc248a1..1348c147e 100644
--- a/limits.c
+++ b/limits.c
@@ -1,8 +1,9 @@
/*
limits.c - code pertaining to limit-switches and performing the homing cycle
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,43 +18,43 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "settings.h"
-#include "protocol.h"
-#include "planner.h"
-#include "stepper.h"
-#include "motion_control.h"
-#include "limits.h"
-#include "report.h"
-
-// Homing axis search distance multiplier. Computed by this value times the axis max travel.
-#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
-
-
-void limits_init()
+
+#include "grbl.h"
+
+
+// Homing axis search distance multiplier. Computed by this value times the cycle travel.
+#ifndef HOMING_AXIS_SEARCH_SCALAR
+ #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
+#endif
+#ifndef HOMING_AXIS_LOCATE_SCALAR
+ #define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared.
+#endif
+
+#ifdef ENABLE_DUAL_AXIS
+ // Flags for dual axis async limit trigger check.
+ #define DUAL_AXIS_CHECK_DISABLE 0 // Must be zero
+ #define DUAL_AXIS_CHECK_ENABLE bit(0)
+ #define DUAL_AXIS_CHECK_TRIGGER_1 bit(1)
+ #define DUAL_AXIS_CHECK_TRIGGER_2 bit(2)
+#endif
+
+void limits_init()
{
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
- if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) {
+ #ifdef DISABLE_LIMIT_PIN_PULL_UP
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
- } else {
+ #else
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
- }
+ #endif
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
} else {
- limits_disable();
+ limits_disable();
}
-
+
#ifdef ENABLE_SOFTWARE_DEBOUNCE
MCUSR &= ~(1<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];
+ #ifdef ENABLE_DUAL_AXIS
+ uint8_t step_pin_dual;
+ uint8_t dual_axis_async_check;
+ int32_t dual_trigger_position;
+ #if (DUAL_AXIS_SELECT == X_AXIS)
+ float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[Y_AXIS];
+ #else
+ float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[X_AXIS];
+ #endif
+ fail_distance = min(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX);
+ fail_distance = max(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN);
+ int32_t dual_fail_distance = trunc(fail_distance*settings.steps_per_mm[DUAL_AXIS_SELECT]);
+ // int32_t dual_fail_distance = trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]);
+ #endif
float target[N_AXIS];
-
- uint8_t limit_pin[N_AXIS], step_pin[N_AXIS];
float max_travel = 0.0;
- for (idx=0; idx settings.max_travel[idx]) { max_travel = settings.max_travel[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]);
+ }
}
- max_travel *= -HOMING_AXIS_SEARCH_SCALAR; // Ensure homing switches engaged by over-estimating max travel.
-
- plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions.
-
+ #ifdef ENABLE_DUAL_AXIS
+ step_pin_dual = (1<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 {
- // Check limit state. Lock out cycle axes when they change.
- limit_state = LIMIT_PIN;
- if (invert_pin) { limit_state ^= LIMIT_MASK; }
- for (idx=0; idx dual_fail_distance) {
+ system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH);
+ mc_reset();
+ protocol_execute_realtime();
+ return;
+ }
+ }
+ } else {
+ dual_axis_async_check |= DUAL_AXIS_CHECK_ENABLE;
+ dual_trigger_position = sys_position[DUAL_AXIS_SELECT];
+ }
+ }
+ #endif
}
- sys.homing_axis_lock = axislock;
+
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
- // Check only for user reset. No time to run protocol_execute_runtime() in this loop.
- if (sys.execute & EXEC_RESET) { protocol_execute_runtime(); return; }
- } while (STEP_MASK & axislock);
-
- st_reset(); // Immediately force kill steppers and reset step segment buffer.
- plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared.
+ // 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)) {
+ 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;
+ } else {
+ // Pull-off motion complete. Disable CYCLE_STOP from executing.
+ system_clear_exec_state_flag(EXEC_CYCLE_STOP);
+ break;
+ }
+ }
+
+ #ifdef ENABLE_DUAL_AXIS
+ } while ((STEP_MASK & axislock) || (sys.homing_axis_lock_dual));
+ #else
+ } while (STEP_MASK & axislock);
+ #endif
+
+ st_reset(); // Immediately force kill steppers and reset step segment buffer.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
// Reverse direction and reset homing rate for locate cycle(s).
- homing_rate = settings.homing_feed_rate;
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;
+ homing_rate = settings.homing_feed_rate;
+ } else {
+ 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
- // default, grbl defines machine space as all negative, as do most CNCs. Since limit switches
+
+ // 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.
+ int32_t set_axis_position;
+ // Set machine positions for homed limit switches. Don't update non-homed axes.
for (idx=0; idx -settings.max_travel[idx]) { soft_limit_error = true; }
- } else {
- if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
- }
- #else
- // NOTE: max_travel is stored as negative
- if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
- #endif
-
- if (soft_limit_error) {
- // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
- // workspace volume so just come to a controlled stop so position is not lost. When complete
- // enter alarm mode.
- if (sys.state == STATE_CYCLE) {
- bit_true_atomic(sys.execute, EXEC_FEED_HOLD);
- do {
- protocol_execute_runtime();
- if (sys.abort) { return; }
- } while ( sys.state != STATE_IDLE || sys.state != STATE_QUEUED);
- }
-
- mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
- bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate soft limit critical event
- protocol_execute_runtime(); // Execute to enter critical event loop and system abort
- return;
+ 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;
}
}
diff --git a/limits.h b/limits.h
index 6ccd70f20..33fe09576 100644
--- a/limits.h
+++ b/limits.h
@@ -1,8 +1,9 @@
/*
limits.h - code pertaining to limit-switches and performing the homing cycle
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,25 +18,24 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
-*/
#ifndef limits_h
-#define limits_h
+#define limits_h
// Initialize the limits module
void limits_init();
+// Disables hard limits.
void limits_disable();
+// Returns limit state as a bit-wise uint8 variable.
+uint8_t limits_get_state();
+
// Perform one portion of the homing cycle based on the input settings.
void limits_go_home(uint8_t cycle_mask);
// Check for soft limit violations
void limits_soft_check(float *target);
-#endif
\ No newline at end of file
+#endif
diff --git a/main.c b/main.c
index 6a57ba540..96f2abae4 100644
--- a/main.c
+++ b/main.c
@@ -1,8 +1,9 @@
/*
main.c - An embedded CNC Controller with rs274/ngc (g-code) support
- Part of Grbl v0.9
-
- Copyright (c) 2012-2014 Sungeun K. Jeon
+ Part of Grbl
+
+ 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
@@ -17,44 +18,43 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "serial.h"
-#include "settings.h"
-#include "protocol.h"
-#include "gcode.h"
-#include "planner.h"
-#include "stepper.h"
-#include "spindle_control.h"
-#include "coolant_control.h"
-#include "motion_control.h"
-#include "limits.h"
-#include "probe.h"
-#include "report.h"
+
+#include "grbl.h"
// Declare system global variable structure
-system_t sys;
+system_t sys;
+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
+ volatile uint8_t sys_rt_exec_debug;
+#endif
int main(void)
{
// Initialize system upon power-up.
serial_init(); // Setup serial baud rate and interrupts
- settings_init(); // Load grbl settings from EEPROM
+ 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(sys)); // Clear all system variables
- sys.abort = true; // Set abort to complete initialization
+
+ memset(sys_position,0,sizeof(sys_position)); // Clear machine position.
sei(); // Enable interrupts
+ // Initialize system state.
+ #ifdef FORCE_INITIALIZATION_ALARM
+ // Force Grbl into an ALARM state upon a power-cycle or hard reset.
+ sys.state = STATE_ALARM;
+ #else
+ sys.state = STATE_IDLE;
+ #endif
+
// Check for power-up and set system alarm if homing is enabled to force homing cycle
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
// startup scripts, but allows access to settings and internal commands. Only a homing
@@ -65,20 +65,31 @@ int main(void)
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { 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 system variables.
+ uint8_t prior_state = sys.state;
+ memset(&sys, 0, sizeof(system_t)); // Clear system struct variable.
+ sys.state = prior_state;
+ sys.f_override = DEFAULT_FEED_OVERRIDE; // Set to 100%
+ sys.r_override = DEFAULT_RAPID_OVERRIDE; // Set to 100%
+ sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE; // Set to 100%
+ memset(sys_probe_position,0,sizeof(sys_probe_position)); // Clear probe position.
+ sys_probe_state = 0;
+ sys_rt_exec_state = 0;
+ sys_rt_exec_alarm = 0;
+ sys_rt_exec_motion_override = 0;
+ sys_rt_exec_accessory_override = 0;
+
// 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.
@@ -87,15 +98,12 @@ int main(void)
plan_sync_position();
gc_sync_position();
- // Reset system variables.
- sys.abort = false;
- sys.execute = 0;
- if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
- else { sys.auto_start = false; }
-
+ // 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 */
}
diff --git a/motion_control.c b/motion_control.c
index 66633d156..6e11e35cb 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -1,9 +1,10 @@
/*
motion_control.c - high level interface for issuing motion commands
- Part of Grbl v0.9
+ Part of Grbl
+
+ Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
+ Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012-2014 Sungeun K. Jeon
-
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@@ -17,97 +18,74 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
- Copyright (c) 2011 Jens Geisler
-*/
-
-#include "system.h"
-#include "settings.h"
-#include "protocol.h"
-#include "gcode.h"
-#include "planner.h"
-#include "stepper.h"
-#include "motion_control.h"
-#include "spindle_control.h"
-#include "coolant_control.h"
-#include "limits.h"
-#include "probe.h"
-#include "report.h"
+
+#include "grbl.h"
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
-// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
+// 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_runtime(); // Check for any run-time commands
+ protocol_execute_realtime(); // Check for any run-time commands
if (sys.abort) { return; } // Bail, if system abort.
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
else { break; }
} while (1);
- #ifdef USE_LINE_NUMBERS
- plan_buffer_line(target, feed_rate, invert_feed_rate, line_number);
- #else
- plan_buffer_line(target, feed_rate, invert_feed_rate);
- #endif
-
- // If idle, indicate to the system there is now a planned block in the buffer ready to cycle
- // start. Otherwise ignore and continue on.
- if (!sys.state) { sys.state = STATE_QUEUED; }
+ // Plan and queue motion into planner buffer
+ if (plan_buffer_line(target, pl_data) == PLAN_EMPTY_BLOCK) {
+ if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
+ // Correctly set spindle state, if there is a coincident position passed. Forces a buffer
+ // sync while in M3 laser mode only.
+ if (pl_data->condition & PL_COND_FLAG_SPINDLE_CW) {
+ spindle_sync(PL_COND_FLAG_SPINDLE_CW, pl_data->spindle_speed);
+ }
+ }
+ }
}
-// 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
+// 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, 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)
-#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];
@@ -115,13 +93,13 @@
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 (gc_state.modal.motion == MOTION_MODE_CW_ARC) { // Correct atan2 output per direction
- if (angular_travel >= 0) { angular_travel -= 2*M_PI; }
+ if (is_clockwise_arc) { // Correct atan2 output per direction
+ if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= 2*M_PI; }
} else {
- if (angular_travel <= 0) { angular_travel += 2*M_PI; }
+ if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += 2*M_PI; }
}
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
@@ -130,13 +108,16 @@
// 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;
+ bit_false(pl_data->condition,PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments.
+ }
+
float theta_per_segment = angular_travel/segments;
float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
@@ -144,26 +125,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;
@@ -175,16 +156,16 @@
float r_axisi;
uint16_t i;
uint8_t count = 0;
-
+
for (i = 1; i 0) {
- // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds.
- protocol_execute_runtime();
- if (sys.abort) { return; }
- _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
- }
+ if (sys.state == STATE_CHECK_MODE) { return; }
+ protocol_buffer_synchronize();
+ delay_sec(seconds, DELAY_MODE_DWELL);
}
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
// executing the homing cycle. This prevents incorrect buffered plans after homing.
-void mc_homing_cycle()
+void mc_homing_cycle(uint8_t cycle_mask)
{
- sys.state = STATE_HOMING; // Set system state variable
+ // 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()) {
+ mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
+ 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
- limits_go_home(HOMING_CYCLE_1); // Homing cycle 1
+ #ifdef HOMING_SINGLE_AXIS_COMMANDS
+ if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask.
+ else
#endif
- #ifdef HOMING_CYCLE_2
- limits_go_home(HOMING_CYCLE_2); // Homing cycle 2
- #endif
-
- protocol_execute_runtime(); // Check for reset and set system abort.
+ {
+ // 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
+ limits_go_home(HOMING_CYCLE_1); // Homing cycle 1
+ #endif
+ #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();
-
- // Set idle state after homing completes and before returning to main program.
- sys.state = STATE_IDLE;
- st_go_idle(); // Set idle state after homing completes
+ plan_sync_position();
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
limits_init();
@@ -275,101 +253,135 @@ void mc_homing_cycle()
// Perform tool length probe cycle. Requires probe switch.
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
-#ifdef USE_LINE_NUMBERS
- void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
-#else
- void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate)
-#endif
-{
+uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags)
+{
// TODO: Need to update this cycle so it obeys a non-auto cycle start.
- if (sys.state == STATE_CHECK_MODE) { return; }
+ if (sys.state == STATE_CHECK_MODE) { return(GC_PROBE_CHECK_MODE); }
// Finish all queued commands and empty planner buffer before starting probe cycle.
protocol_buffer_synchronize();
- uint8_t auto_start_state = sys.auto_start; // Store run state
-
+ if (sys.abort) { return(GC_PROBE_ABORT); } // Return if system reset has been issued.
+
+ // Initialize probing control variables
+ uint8_t is_probe_away = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_AWAY);
+ uint8_t is_no_error = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_NO_ERROR);
+ 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.
- if (probe_get_state()) {
- bit_true_atomic(sys.execute, EXEC_CRIT_EVENT);
- protocol_execute_runtime();
+ // NOTE: This probe initialization error applies to all probing cycles.
+ if ( probe_get_state() ) { // Check probe pin state.
+ system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_INITIAL);
+ protocol_execute_realtime();
+ probe_configure_invert_mask(false); // Re-initialize invert mask before returning.
+ return(GC_PROBE_FAIL_INIT); // Nothing else to do but bail.
}
- 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
-
- // Activate the probing monitor in the stepper module.
- sys.probe_state = PROBE_ACTIVE;
+ 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.
- bit_true_atomic(sys.execute, EXEC_CYCLE_START);
+ system_set_exec_state_flag(EXEC_CYCLE_START);
do {
- protocol_execute_runtime();
- if (sys.abort) { return; } // Check for system abort
- } while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED));
+ protocol_execute_realtime();
+ if (sys.abort) { return(GC_PROBE_ABORT); } // Check for system abort
+ } while (sys.state != STATE_IDLE);
+
+ // Probing cycle complete!
- // Probing motion complete. If the probe has not been triggered, error out.
- if (sys.probe_state == PROBE_ACTIVE) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); }
- protocol_execute_runtime(); // Check and execute run-time commands
- if (sys.abort) { return; } // Check for system abort
+ // 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(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.
+ probe_configure_invert_mask(false); // Re-initialize invert mask.
+ protocol_execute_realtime(); // Check and execute run-time commands
// 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.
-
- // Pull-off triggered probe to the trigger location since we had to decelerate a little beyond
- // it to stop the machine in a controlled manner.
- uint8_t idx;
- for(idx=0; idx.
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef motion_control_h
#define motion_control_h
-#define HOMING_CYCLE_LINE_NUMBER -1
+
+// System motion commands must have a line number of zero.
+#define HOMING_CYCLE_LINE_NUMBER 0
+#define PARKING_MOTION_LINE_NUMBER 0
+
+#define HOMING_CYCLE_ALL 0 // Must be zero.
+#define HOMING_CYCLE_X bit(X_AXIS)
+#define HOMING_CYCLE_Y bit(Y_AXIS)
+#define HOMING_CYCLE_Z bit(Z_AXIS)
+
// 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, isclockwise boolean. Used
+// 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, 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);
-#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);
// Perform homing cycle to locate machine zero. Requires limit switches.
-void mc_homing_cycle();
+void mc_homing_cycle(uint8_t cycle_mask);
// 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, int32_t line_number);
-#else
-void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate);
-#endif
+uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags);
+
+// Handles updating the override control state.
+void mc_override_ctrl_update(uint8_t override_state);
+
+// Plans and executes the single special motion case for parking. Independent of main planner buffer.
+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();
diff --git a/nuts_bolts.c b/nuts_bolts.c
index 785372506..9d89a8d06 100644
--- a/nuts_bolts.c
+++ b/nuts_bolts.c
@@ -1,8 +1,9 @@
/*
nuts_bolts.c - Shared functions
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,15 +18,8 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-#include "system.h"
-#include "print.h"
+#include "grbl.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
@@ -36,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 == '-') {
@@ -54,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;
@@ -77,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 {
@@ -109,26 +103,44 @@ 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);
}
+// Non-blocking delay function used for general operation and suspend features.
+void delay_sec(float seconds, uint8_t mode)
+{
+ uint16_t i = ceil(1000/DWELL_TIME_STEP*seconds);
+ while (i-- > 0) {
+ if (sys.abort) { return; }
+ if (mode == DELAY_MODE_DWELL) {
+ protocol_execute_realtime();
+ } 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.
+ }
+ _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
+ }
+}
+
+
// 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) {
@@ -147,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.
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef nuts_bolts_h
#define nuts_bolts_h
@@ -31,40 +25,54 @@
#define false 0
#define true 1
-#define N_AXIS 4 // 4 // Number of axes
-#define X_AXIS 0 // Axis indexing value. Must start with 0 and be continuous.
+#define SOME_LARGE_VALUE 1.0E+38
+
+// Axis array index values. Must start with 0 and be continuous.
+#define N_AXIS 4 // Number of axes
+#define X_AXIS 0 // Axis indexing value.
#define Y_AXIS 1
#define Z_AXIS 2
#define C_AXIS 3
// #define A_AXIS 3
+// CoreXY motor assignments. DO NOT ALTER.
+// NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations.
+#ifdef COREXY
+ #define A_MOTOR X_AXIS // Must be X_AXIS
+ #define B_MOTOR Y_AXIS // Must be Y_AXIS
+#endif
+
+// Conversions
#define MM_PER_INCH (25.40)
#define INCH_PER_MM (0.0393701)
-
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
+#define DELAY_MODE_DWELL 0
+#define DELAY_MODE_SYS_SUSPEND 1
+
// Useful macros
#define clear_vector(a) memset(a, 0, sizeof(a))
#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)
// #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS)
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define isequal_position_vector(a,b) !(memcmp(a, b, sizeof(float)*N_AXIS))
// Bit field and masking macros
-#define bit(n) (1 << n)
-#define bit_true_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (mask); SREG = sreg; }
-#define bit_false_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) &= ~(mask); SREG = sreg; }
-#define bit_toggle_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) ^= (mask); SREG = sreg; }
+#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);
+// Non-blocking delay function used for general operation and suspend features.
+void delay_sec(float seconds, uint8_t mode);
+
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
void delay_ms(uint16_t ms);
@@ -74,4 +82,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
diff --git a/planner.c b/planner.c
index 21e4181ff..49ff72291 100644
--- a/planner.c
+++ b/planner.c
@@ -1,9 +1,11 @@
/*
planner.c - buffers movement commands and manages the acceleration profile plan
- Part of Grbl v0.9
+ Part of Grbl
+
+ Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
+ Copyright (c) 2009-2011 Simen Svale Skogsrud
+ Copyright (c) 2011 Jens Geisler
- Copyright (c) 2012-2014 Sungeun K. Jeon
-
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
@@ -17,23 +19,9 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
- Copyright (c) 2011 Jens Geisler
-*/
-
-#include "system.h"
-#include "planner.h"
-#include "protocol.h"
-#include "stepper.h"
-#include "settings.h"
+#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
@@ -47,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; }
@@ -62,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--;
@@ -70,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
@@ -135,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.
@@ -152,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) {
@@ -176,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.
@@ -197,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()
{
- memset(&pl, 0, sizeof(pl)); // Clear planner struct
block_buffer_tail = 0;
block_buffer_head = 0; // Empty = tail
next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
@@ -218,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 );
@@ -229,18 +223,26 @@ void plan_discard_current_block()
}
-plan_block_t *plan_get_current_block()
+// Returns address of planner buffer block used by system motions. Called by segment generator.
+plan_block_t *plan_get_system_motion_block()
{
- if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
+ return(&block_buffer[block_buffer_head]);
+}
+
+
+// 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
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 );
}
@@ -252,165 +254,256 @@ 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 = block->programmed_rate;
+ if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); }
+ else {
+ 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). */
-#ifdef USE_LINE_NUMBERS
- void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number)
-#else
- void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate)
-#endif
+ 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 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];
+ 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 (block->condition & PL_COND_FLAG_SYSTEM_MOTION) {
+ #ifdef COREXY
+ position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
+ position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
+ position_steps[Z_AXIS] = sys_position[Z_AXIS];
+ #else
+ memcpy(position_steps, sys_position, sizeof(sys_position));
+ #endif
+ } 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]);
+ block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
+ block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
+ #endif
+
for (idx=0; idxsteps[idx] = labs(target_steps[idx]-pl.position[idx]);
- block->step_event_count = max(block->step_event_count, block->steps[idx]);
-
- // Compute individual axes distance for move and prep unit vector calculations.
+ // Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
+ // Also, compute individual axes distance for move and prep unit vector calculations.
// NOTE: Computes true distance from converted step values.
- delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx];
- unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later.
-
+ #ifdef COREXY
+ if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
+ target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
+ block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
+ }
+ 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];
+ } 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];
+ } else {
+ delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
+ }
+ #else
+ target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
+ block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
+ 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
+
// 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;
+ if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
}
- 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; }
-
- // 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/feed_rate; }
- if (feed_rate < MINIMUM_FEED_RATE) { feed_rate = MINIMUM_FEED_RATE; } // Prevents step generation round-off condition.
-
- // 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.
+ 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;
- for (idx=0; idxacceleration = 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];
- }
+ 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);
+
+ // Store programmed rate.
+ if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
+ else {
+ 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) {
-
+ 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 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.
-
- 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).
- float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
+ // 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; idxmax_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
- (block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
+ // 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.
+ block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
+ } else {
+ 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));
-
- // Update previous path unit_vector and nominal speed (squared)
- 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;
+ // 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 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;
- next_buffer_head = plan_next_block_index(block_buffer_head);
-
- // Finish up by recalculating the plan with the new block.
- planner_recalculate();
+ // Update previous path unit_vector and planner position.
+ memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
+ 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;
+ next_buffer_head = plan_next_block_index(block_buffer_head);
+
+ // Finish up by recalculating the plan with the new block.
+ planner_recalculate();
+ }
+ return(PLAN_OK);
}
// Reset the planner position vectors. Called by the system abort/initialization routine.
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.
uint8_t idx;
for (idx=0; idx= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); }
+ return((block_buffer_tail-block_buffer_head-1));
+}
+
+
// Returns the number of active blocks are in the planner buffer.
+// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
uint8_t plan_get_block_buffer_count()
{
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
@@ -425,5 +518,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();
}
diff --git a/planner.h b/planner.h
index 5385163b3..e3751e169 100644
--- a/planner.h
+++ b/planner.h
@@ -1,8 +1,9 @@
/*
planner.h - buffers movement commands and manages the acceleration profile plan
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,12 +18,6 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef planner_h
#define planner_h
@@ -31,53 +26,93 @@
// 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) // Interprets feed rate value as inverse time when set.
+#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)
+#define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION|PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE)
+#define PL_COND_SPINDLE_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW)
+#define PL_COND_ACCESSORY_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW|PL_COND_FLAG_COOLANT_FLOOD|PL_COND_FLAG_COOLANT_MIST)
+
+
// 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.
-
- // Fields used by the motion planner to manage acceleration
- 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
+ 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;
+ 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 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.
+
+ // 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;
-
+
+// 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();
+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
+// 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
- void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number);
-#else
- void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate);
-#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 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();
@@ -85,7 +120,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();
@@ -93,10 +134,17 @@ void plan_sync_position();
// Reinitialize plan with a partially completed block
void plan_cycle_reinitialize();
+// Returns the number of available blocks are in the planner buffer.
+uint8_t plan_get_block_buffer_available();
+
// Returns the number of active blocks are in the planner buffer.
+// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
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
diff --git a/print.c b/print.c
index b398e824a..771e39969 100644
--- a/print.c
+++ b/print.c
@@ -1,8 +1,9 @@
/*
print.c - Functions for formatting output strings
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,16 +18,8 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-#include "system.h"
-#include "serial.h"
-#include "settings.h"
+#include "grbl.h"
void printString(const char *s)
@@ -46,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] :
@@ -67,56 +60,55 @@ void printPgmString(const char *s)
// }
-void print_uint8_base2(uint8_t n)
-{
- unsigned char buf[8];
- uint8_t i = 0;
-
- for (; i < 8; i++) {
- buf[i] = n & 1;
- n >>= 1;
- }
-
- for (; i > 0; i--)
- serial_write('0' + buf[i - 1]);
+// 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); }
}
-void print_uint8_base10(uint8_t n)
-{
- if (n == 0) {
- serial_write('0');
- return;
- }
-
- unsigned char buf[3];
+// 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;
- while (n > 0) {
- buf[i++] = n % 10 + '0';
- n /= 10;
+ for (; i < digits; i++) {
+ buf[i] = n % 2 ;
+ n /= 2;
}
for (; i > 0; i--)
- serial_write(buf[i - 1]);
+ serial_write('0' + buf[i - 1]);
}
-void print_uint32_base10(unsigned long n)
-{
+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]);
}
@@ -126,7 +118,7 @@ void printInteger(long n)
{
if (n < 0) {
serial_write('-');
- print_uint32_base10((-n));
+ print_uint32_base10(-n);
} else {
print_uint32_base10(n);
}
@@ -136,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)
{
@@ -152,28 +144,27 @@ 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;
- buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
+ uint32_t a = (long)n;
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--)
+ for (; i > 0; i--) {
+ if (i == decimal_places) { serial_write('.'); } // Insert decimal point in right place.
serial_write(buf[i-1]);
+ }
}
@@ -181,16 +172,15 @@ void printFloat(float n, uint8_t decimal_places)
// in the config.h.
// - 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 {
@@ -198,15 +188,13 @@ void printFloat_RateValue(float n) {
}
}
-void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); }
-
-
-// Debug tool to print free memory in bytes at the called point. Not used otherwise.
-void printFreeMemory()
-{
- extern int __heap_start, *__brkval;
- uint16_t free; // Up to 64k values.
- free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
- printInteger((int32_t)free);
- printString(" ");
-}
+// 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;
+// uint16_t free; // Up to 64k values.
+// free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
+// printInteger((int32_t)free);
+// printString(" ");
+// }
diff --git a/print.h b/print.h
index 845c80c3f..31e0a576c 100644
--- a/print.h
+++ b/print.h
@@ -1,8 +1,9 @@
/*
print.h - Functions for formatting output strings
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,12 +18,6 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef print_h
#define print_h
@@ -36,23 +31,21 @@ void printInteger(long n);
void print_uint32_base10(uint32_t n);
-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.)
void printFloat_CoordValue(float n);
-
void printFloat_RateValue(float n);
-void printFloat_SettingValue(float n);
-
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory();
-#endif
\ No newline at end of file
+#endif
diff --git a/probe.c b/probe.c
index 00dde4766..60c9073ad 100644
--- a/probe.c
+++ b/probe.c
@@ -1,8 +1,8 @@
/*
probe.c - code pertaining to probing methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2014 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,26 +17,35 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-
-#include "system.h"
-#include "settings.h"
-#include "probe.h"
-// Inverts the probe pin state depending on user settings.
+#include "grbl.h"
+
+
+// Inverts the probe pin state depending on user settings and probing cycle mode.
uint8_t probe_invert_mask;
// Probe pin initialization routine.
-void probe_init()
+void probe_init()
{
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
- if (bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)) {
+ #ifdef DISABLE_PROBE_PIN_PULL_UP
PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down.
- probe_invert_mask = 0;
- } else {
+ #else
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
- probe_invert_mask = PROBE_MASK;
- }
+ #endif
+ 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.
+void probe_configure_invert_mask(uint8_t is_probe_away)
+{
+ probe_invert_mask = 0; // Initialize as zero.
+ if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; }
+ if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; }
}
@@ -49,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(float)*N_AXIS);
- bit_true(sys.execute, EXEC_FEED_HOLD);
- }
+ 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);
}
}
diff --git a/probe.h b/probe.h
index 81fb55bf2..03d5fd329 100644
--- a/probe.h
+++ b/probe.h
@@ -1,8 +1,8 @@
/*
probe.h - code pertaining to probing methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2014 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,19 +17,23 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-
+
#ifndef probe_h
-#define probe_h
+#define probe_h
-// Values that define the probing state machine.
-#define PROBE_OFF 0 // No probing. (Must be zero.)
+// 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();
-// Returns probe pin state.
+// 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.
uint8_t probe_get_state();
// Monitors probe pin state and records the system position when detected. Called by the
diff --git a/protocol.c b/protocol.c
index 897832525..08ea48bf2 100644
--- a/protocol.c
+++ b/protocol.c
@@ -1,8 +1,9 @@
/*
protocol.c - controls Grbl execution protocol and procedures
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,111 +18,108 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "serial.h"
-#include "settings.h"
-#include "protocol.h"
-#include "gcode.h"
-#include "planner.h"
-#include "stepper.h"
-#include "motion_control.h"
-#include "report.h"
+#include "grbl.h"
-static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
-
-
-// Directs and executes one line of formatted input from protocol_process. While mostly
-// incoming streaming g-code blocks, this also directs and executes Grbl internal commands,
-// such as settings, initiating the homing cycle, and toggling switch states.
-static void protocol_execute_line(char *line)
-{
- protocol_execute_runtime(); // Runtime command check point.
- if (sys.abort) { return; } // Bail to calling function upon system abort
+// Define line flags. Includes comment type tracking and line overflow detection.
+#define LINE_FLAG_OVERFLOW bit(0)
+#define LINE_FLAG_COMMENT_PARENTHESES bit(1)
+#define LINE_FLAG_COMMENT_SEMICOLON bit(2)
- if (line[0] == 0) {
- // Empty or comment line. Send status message 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);
+static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
- } else {
- // Parse and execute g-code block!
- report_status_message(gc_execute_line(line));
- }
-}
+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);
+ // NOTE: Sleep mode disables the stepper drivers and position can't be guaranteed.
+ // Re-initialize the sleep state as an ALARM mode to ensure user homes or acknowledges.
+ if (sys.state & (STATE_ALARM | STATE_SLEEP)) {
+ report_feedback_message(MESSAGE_ALARM_LOCK);
+ sys.state = STATE_ALARM; // Ensure alarm state is set.
} else {
+ // 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!
- sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.
system_execute_startup(line); // Execute startup script.
}
-
- // ---------------------------------------------------------------------------------
- // Primary loop! Upon a system abort, this exits back to main() to reset the system.
- // ---------------------------------------------------------------------------------
-
- uint8_t iscomment = false;
+
+ // ---------------------------------------------------------------------------------
+ // 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;
for (;;) {
// 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
+
line[char_counter] = 0; // Set string termination character.
- protocol_execute_line(line); // Line is complete. Execute it!
- iscomment = false;
+ #ifdef REPORT_ECHO_LINE_RECEIVED
+ report_echo_line_received(line);
+ #endif
+
+ // 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);
+ } 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 | 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));
+ }
+
+ // Reset tracking data for next line.
+ line_flags = 0;
char_counter = 0;
+
} else {
- if (iscomment) {
- // Throw away all comment characters
+
+ if (line_flags) {
+ // Throw away all (except EOL) comment characters and overflow characters.
if (c == ')') {
- // End of comment. Resume line.
- iscomment = false;
+ // End of '()' comment. Resume line allowed.
+ 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 == '(') {
@@ -129,178 +127,639 @@ void protocol_main_loop()
// NOTE: This doesn't follow the NIST definition exactly, but is good enough for now.
// In the future, we could simply remove the items within the comments, but retain the
// comment control characters, so that the g-code parser can error-check it.
- iscomment = true;
- // } else if (c == ';') {
- // Comment character to EOL NOT SUPPORTED. LinuxCNC definition. Not NIST.
-
- // TODO: Install '%' feature
+ line_flags |= LINE_FLAG_COMMENT_PARENTHESES;
+ } else if (c == ';') {
+ // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
+ line_flags |= LINE_FLAG_COMMENT_SEMICOLON;
+ // 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)) {
- // Detect line buffer overflow. Report error and reset line buffer.
- report_status_message(STATUS_OVERFLOW);
- iscomment = false;
- char_counter = 0;
+ // Detect line buffer overflow and set flag.
+ line_flags |= LINE_FLAG_OVERFLOW;
} else if (c >= 'a' && c <= 'z') { // Upcase lowercase
line[char_counter++] = c-'a'+'A';
} else {
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_runtime(); // Runtime command check point.
+ protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to main() program loop to reset system.
-
}
-
+
return; /* Never reached */
}
-// Executes run-time commands, when required. This is called from various check points in the main
-// program, primarily where there may be a while loop waiting for a buffer to clear space or any
-// point where the execution time from the last check point may be more than a fraction of a second.
-// This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code
-// parsing and planning functions. This function also serves as an interface for the interrupts to
-// set the system runtime flags, where only the main program handles them, removing the need to
-// define more computationally-expensive volatile variables. This also provides a controlled way to
-// execute certain tasks without having two or more instances of the same task, such as the planner
-// recalculating the buffer upon a feedhold or override.
-// NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts,
+// Block until all buffered steps are executed or in a cycle state. Works with feed hold
+// during a synchronize call, if it should happen. Also, waits for clean cycle end.
+void protocol_buffer_synchronize()
+{
+ // If system is queued, ensure cycle resumes if the auto start flag is present.
+ protocol_auto_cycle_start();
+ do {
+ protocol_execute_realtime(); // Check and execute run-time commands
+ if (sys.abort) { return; } // Check for system abort
+ } while (plan_get_current_block() || (sys.state == STATE_CYCLE));
+}
+
+
+// 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()
+{
+ 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
+// (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
+// 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.
-void protocol_execute_runtime()
+void protocol_execute_realtime()
+{
+ protocol_exec_rt_system();
+ if (sys.suspend) { protocol_exec_rt_suspend(); }
+}
+
+
+// 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()
{
- uint8_t rt_exec = sys.execute; // Copy to avoid calling volatile multiple times
+ uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
+ rt_exec = sys_rt_exec_alarm; // Copy volatile sys_rt_exec_alarm.
if (rt_exec) { // Enter only if any bit flag is true
-
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
// the source of the error to the user. If critical, Grbl disables by entering an infinite
// loop until system reset/abort.
- if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) {
- sys.state = STATE_ALARM; // Set system alarm state
-
- // Critical events. Hard/soft limit events identified by both critical event and alarm exec
- // flags. Probe fail is identified by the critical event exec flag only.
- if (rt_exec & EXEC_CRIT_EVENT) {
- if (rt_exec & EXEC_ALARM) { report_alarm_message(ALARM_LIMIT_ERROR); }
- else { report_alarm_message(ALARM_PROBE_FAIL); }
- report_feedback_message(MESSAGE_CRITICAL_EVENT);
- bit_false_atomic(sys.execute,EXEC_RESET); // Disable any existing reset
- do {
- // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits
- // typically occur while unattended or not paying attention. Gives the user time
- // to do what is needed before resetting, like killing the incoming stream. The
- // same could be said about soft limits. While the position is not lost, the incoming
- // stream could be still engaged and cause a serious crash if it continues afterwards.
- } while (bit_isfalse(sys.execute,EXEC_RESET));
-
- // Standard alarm event. Only abort during motion qualifies.
- } else {
- // Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the
- // user that position may have been lost and set alarm state to enable the alarm lockout
- // to indicate the possible severity of the problem.
- report_alarm_message(ALARM_ABORT_CYCLE);
- }
- bit_false_atomic(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT));
- }
-
- // Execute system abort.
+ sys.state = STATE_ALARM; // Set system alarm state
+ report_alarm_message(rt_exec);
+ // Halt everything upon a critical event flag. Currently hard and soft limits flag this.
+ if ((rt_exec == EXEC_ALARM_HARD_LIMIT) || (rt_exec == EXEC_ALARM_SOFT_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
+ // 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, 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(); // Clear alarm
+ }
+
+ rt_exec = sys_rt_exec_state; // Copy volatile sys_rt_exec_state.
+ if (rt_exec) {
+
+ // 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();
- bit_false_atomic(sys.execute,EXEC_STATUS_REPORT);
+ system_clear_exec_state_flag(EXEC_STATUS_REPORT);
}
-
- // Execute a feed hold with deceleration, only during cycle.
- if (rt_exec & EXEC_FEED_HOLD) {
- // !!! During a cycle, the segment buffer has just been reloaded and full. So the math involved
- // with the feed hold should be fine for most, if not all, operational scenarios.
- if (sys.state == STATE_CYCLE) {
- sys.state = STATE_HOLD;
- st_update_plan_block_parameters();
- st_prep_buffer();
- sys.auto_start = false; // Disable planner auto start upon feed hold.
+
+ // 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 | EXEC_SLEEP)) {
+
+ // State check for allowable states for hold methods.
+ if (!(sys.state & (STATE_ALARM | STATE_CHECK_MODE))) {
+
+ // 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) { // Jog cancelled upon any hold event, except for sleeping.
+ if (!(rt_exec & EXEC_SLEEP)) { sys.suspend |= SUSPEND_JOG_CANCEL; }
+ }
+ }
+ }
+ // If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
+ if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_HOLD_COMPLETE; }
+
+ // 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. Motion cancel is valid for a single planner block motion only, while jog cancel
+ // will handle and clear multiple planner block motions.
+ if (!(sys.state & STATE_JOG)) { sys.suspend |= SUSPEND_MOTION_CANCEL; } // NOTE: State is STATE_CYCLE.
+ }
+
+ // Execute a feed hold with deceleration, if required. Then, suspend system.
+ if (rt_exec & EXEC_FEED_HOLD) {
+ // Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
+ if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG | STATE_SLEEP))) { 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);
+ // 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, parked or in sleep state.
+ if (sys.state == STATE_SAFETY_DOOR) {
+ 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;
+ }
+ }
+ if (sys.state != STATE_SLEEP) { 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;
+ }
+
}
- bit_false_atomic(sys.execute,EXEC_FEED_HOLD);
+
+ if (rt_exec & EXEC_SLEEP) {
+ if (sys.state == STATE_ALARM) { sys.suspend |= (SUSPEND_RETRACT_COMPLETE|SUSPEND_HOLD_COMPLETE); }
+ sys.state = STATE_SLEEP;
+ }
+
+ system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR | EXEC_SLEEP));
}
-
- // Execute a cycle start by starting the stepper interrupt begin executing the blocks in queue.
- if (rt_exec & EXEC_CYCLE_START) {
- if (sys.state == STATE_QUEUED) {
- sys.state = STATE_CYCLE;
- st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
- st_wake_up();
- if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) {
- sys.auto_start = true; // Re-enable auto start after feed hold.
- } else {
- sys.auto_start = false; // Reset auto start per settings.
+
+ // 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))) {
+ // 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.
+ if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
+ if (sys.state == STATE_HOLD && sys.spindle_stop_ovr) {
+ sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_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)) {
+ 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;
+ }
+ }
}
- }
- bit_false_atomic(sys.execute,EXEC_CYCLE_START);
+ }
+ system_clear_exec_state_flag(EXEC_CYCLE_START);
}
-
- // Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
- // runtime 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.
- // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
+
if (rt_exec & EXEC_CYCLE_STOP) {
- if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; }
- else { sys.state = STATE_IDLE; }
- bit_false_atomic(sys.execute,EXEC_CYCLE_STOP);
+ // 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.
+ // 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|STATE_SLEEP)) && !(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_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);
}
+ }
+
+ // 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 = 0; // Set to report change immediately
+ plan_update_velocity_profile_parameters();
+ plan_cycle_reinitialize();
+ }
}
-
- // Overrides flag byte (sys.override) and execution should be installed here, since they
- // are runtime and require a direct and controlled interface to the main stepper program.
+
+ 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;
+ // NOTE: Spindle speed overrides during HOLD state are taken care of by suspend function.
+ if (sys.state == STATE_IDLE) { spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); }
+ else { bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); }
+ sys.report_ovr_counter = 0; // Set to report change immediately
+ }
+
+ if (rt_exec & EXEC_SPINDLE_OVR_STOP) {
+ // Spindle stop override allowed only while in HOLD state.
+ // NOTE: Report counters are set in spindle_set_state() when spindle stop is executed.
+ if (sys.state == STATE_HOLD) {
+ if (!(sys.spindle_stop_ovr)) { sys.spindle_stop_ovr = SPINDLE_STOP_OVR_INITIATE; }
+ else if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_ENABLED) { sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE; }
+ }
+ }
+
+ // NOTE: Since coolant state always performs a planner sync whenever it changes, the current
+ // run state can be determined by checking the parser state.
+ // NOTE: Coolant overrides only operate during IDLE, CYCLE, HOLD, and JOG states. Ignored otherwise.
+ if (rt_exec & (EXEC_COOLANT_FLOOD_OVR_TOGGLE | EXEC_COOLANT_MIST_OVR_TOGGLE)) {
+ if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_JOG))) {
+ 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; }
+ }
+ 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; }
+ }
+ #else
+ if (coolant_state & COOLANT_FLOOD_ENABLE) { bit_false(coolant_state,COOLANT_FLOOD_ENABLE); }
+ else { coolant_state |= COOLANT_FLOOD_ENABLE; }
+ #endif
+ coolant_set_state(coolant_state); // Report counter set in coolant_set_state().
+ gc_state.modal.coolant = coolant_state;
+ }
+ }
+ }
+
+ #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_HOMING)) { st_prep_buffer(); }
-
-}
+ if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_SLEEP| STATE_JOG)) {
+ st_prep_buffer();
+ }
+
+}
-// Block until all buffered steps are executed or in a cycle state. Works with feed hold
-// during a synchronize call, if it should happen. Also, waits for clean cycle end.
-void protocol_buffer_synchronize()
+// Handles Grbl system suspend procedures, such as feed hold, safety door, and parking motion.
+// 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
+static void protocol_exec_rt_suspend()
{
- // If system is queued, ensure cycle resumes if the auto start flag is present.
- protocol_auto_cycle_start();
- // Check and set auto start to resume cycle after synchronize and caller completes.
- if (sys.state == STATE_CYCLE) { sys.auto_start = true; }
- while (plan_get_current_block() || (sys.state == STATE_CYCLE)) {
- protocol_execute_runtime(); // Check and execute run-time commands
- if (sys.abort) { return; } // Check for system abort
- }
-}
+ #ifdef PARKING_ENABLE
+ // Declare and initialize parking local variables
+ 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 & PL_COND_SPINDLE_MASK) | coolant_get_state();
+ restore_spindle_speed = block->spindle_speed;
+ }
+ #ifdef DISABLE_LASER_DURING_HOLD
+ if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
+ system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP);
+ }
+ #endif
+ #else
+ if (block == NULL) { restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant); }
+ else { restore_condition = (block->condition & PL_COND_SPINDLE_MASK) | coolant_get_state(); }
+ #endif
+
+ while (sys.suspend) {
+
+ if (sys.abort) { return; }
+
+ // Block until initial hold is complete and the machine has stopped motion.
+ if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
+ // Parking manager. Handles de/re-energizing, switch state checks, and parking motions for
+ // the safety door and sleep states.
+ if (sys.state & (STATE_SAFETY_DOOR | STATE_SLEEP)) {
+
+ // Handles retraction motions and de-energizing.
+ if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
-// 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 and mc_line() only and executes when one of
-// two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished,
-// single commands), or the planner buffer is full and ready to go.
-void protocol_auto_cycle_start() { if (sys.auto_start) { bit_true_atomic(sys.execute, EXEC_CYCLE_START); } }
+ // Ensure any prior spindle stop override is disabled at start of safety door routine.
+ sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED;
+
+ #ifndef PARKING_ENABLE
+
+ spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
+ coolant_set_state(COOLANT_DISABLE); // 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);
+ }
+
+ // Execute slow pull-out parking retract motion. Parking requires homing enabled, the
+ // current location not exceeding the parking target location, and laser mode disabled.
+ // NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
+ (parking_target[PARKING_AXIS] < PARKING_TARGET) &&
+ bit_isfalse(settings.flags,BITFLAG_LASER_MODE) &&
+ (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
+ #else
+ if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
+ (parking_target[PARKING_AXIS] < PARKING_TARGET) &&
+ bit_isfalse(settings.flags,BITFLAG_LASER_MODE)) {
+ #endif
+ // 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;
+ pl_data->feed_rate = PARKING_PULLOUT_RATE;
+ pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Retain accessory state
+ pl_data->spindle_speed = restore_spindle_speed;
+ mc_parking_motion(parking_target, pl_data);
+ }
+
+ // NOTE: Clear accessory state after retract and after an aborted restore motion.
+ pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
+ pl_data->spindle_speed = 0.0;
+ spindle_set_state(SPINDLE_DISABLE,0.0); // 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.
+ // NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
+ spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
+ coolant_set_state(COOLANT_DISABLE); // De-energize
+
+ }
+
+ #endif
+
+ sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
+ sys.suspend |= SUSPEND_RETRACT_COMPLETE;
+
+ } else {
+
+
+ if (sys.state == STATE_SLEEP) {
+ report_feedback_message(MESSAGE_SLEEP_MODE);
+ // Spindle and coolant should already be stopped, but do it again just to be sure.
+ spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
+ coolant_set_state(COOLANT_DISABLE); // De-energize
+ st_go_idle(); // Disable steppers
+ while (!(sys.abort)) { protocol_exec_rt_system(); } // Do nothing until reset.
+ return; // Abort received. Return to re-initialize.
+ }
+
+ // Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to 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.
+ }
+ }
+
+ // 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.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
+ (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
+ #else
+ if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
+ #endif
+ // 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)) {
+ if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
+ // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
+ bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
+ } else {
+ 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)) {
+ // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
+ coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST)));
+ delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
+ }
+ }
+
+ #ifdef PARKING_ENABLE
+ // Execute slow plunge motion from pull-out position to resume position.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
+ (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
+ #else
+ if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
+ #endif
+ // 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;
+ pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state
+ pl_data->spindle_speed = restore_spindle_speed;
+ mc_parking_motion(restore_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.
+ }
+ }
+
+ }
+
+
+ } 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.spindle_stop_ovr) {
+ // Handles beginning of spindle stop
+ if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_INITIATE) {
+ if (gc_state.modal.spindle != SPINDLE_DISABLE) {
+ spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
+ sys.spindle_stop_ovr = SPINDLE_STOP_OVR_ENABLED; // Set stop override state to enabled, if de-energized.
+ } else {
+ sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
+ }
+ // Handles restoring of spindle state
+ } else if (sys.spindle_stop_ovr & (SPINDLE_STOP_OVR_RESTORE | SPINDLE_STOP_OVR_RESTORE_CYCLE)) {
+ if (gc_state.modal.spindle != SPINDLE_DISABLE) {
+ report_feedback_message(MESSAGE_SPINDLE_RESTORE);
+ if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
+ // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
+ bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
+ } else {
+ spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
+ }
+ }
+ if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_RESTORE_CYCLE) {
+ system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
+ }
+ sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
+ }
+ } else {
+ // Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state.
+ // NOTE: STEP_CONTROL_UPDATE_SPINDLE_PWM is automatically reset upon resume in step generator.
+ if (bit_istrue(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM)) {
+ spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
+ bit_false(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
+ }
+ }
+
+ }
+ }
+
+ protocol_exec_rt_system();
+
+ }
+}
diff --git a/protocol.h b/protocol.h
index 5a752fd1a..7bc6e92b3 100644
--- a/protocol.h
+++ b/protocol.h
@@ -1,8 +1,9 @@
/*
protocol.h - controls Grbl execution protocol and procedures
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,12 +18,6 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef protocol_h
#define protocol_h
@@ -31,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
@@ -41,17 +36,9 @@
// them as they complete. It is also responsible for finishing the initialization procedures.
void protocol_main_loop();
-// Checks and executes a runtime command at various stop points in main program
-void protocol_execute_runtime();
-
-// 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();
+// Checks and executes a realtime command at various stop points in main program
+void protocol_execute_realtime();
+void protocol_exec_rt_system();
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start();
diff --git a/report.c b/report.c
index 834229afe..8b88971b9 100644
--- a/report.c
+++ b/report.c
@@ -1,445 +1,709 @@
-/*
- report.c - reporting and messaging methods
- Part of Grbl v0.9
-
- Copyright (c) 2012-2014 Sungeun K. Jeon
- Copyright (c) 2014 Bob Beattie
-
- Grbl is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Grbl is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Grbl. If not, see .
-*/
-
-/*
- This file functions as the primary feedback interface for Grbl. Any outgoing data, such
- as the protocol status messages, feedback messages, and status reports, are stored here.
- For the most part, these functions primarily are called from protocol.c methods. If a
- different style feedback is desired (i.e. JSON), then a user can change these following
- methods to accomodate their needs.
-*/
-
-#include "system.h"
-#include "report.h"
-#include "print.h"
-#include "settings.h"
-#include "gcode.h"
-#include "coolant_control.h"
-#include "planner.h"
-#include "spindle_control.h"
-#include "stepper.h"
-#include "serial.h"
-
-
-// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
-// For every incoming line, this method responds with an 'ok' for a successful command or an
-// 'error:' to indicate some error event with the line or some critical system error during
-// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
-// from a critical error, such as a triggered hard limit. Interface should always monitor for these
-// responses.
-// NOTE: In silent mode, all error codes are greater than zero.
-// TODO: Install silent mode to return only numeric values, primarily for GUIs.
-void report_status_message(uint8_t status_code)
-{
- if (status_code == 0) { // STATUS_OK
- printPgmString(PSTR("ok\r\n"));
- } else {
- printPgmString(PSTR("error: "));
- switch(status_code) {
- case STATUS_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;
-
- // 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
- }
- printPgmString(PSTR("\r\n"));
- }
-}
-
-// Prints alarm messages.
-void report_alarm_message(int8_t alarm_code)
-{
- printPgmString(PSTR("ALARM: "));
- switch (alarm_code) {
- case ALARM_LIMIT_ERROR:
- printPgmString(PSTR("Hard/soft limit")); break;
- case ALARM_ABORT_CYCLE:
- printPgmString(PSTR("Abort during cycle")); break;
- case ALARM_PROBE_FAIL:
- printPgmString(PSTR("Probe fail")); break;
- }
- printPgmString(PSTR("\r\n"));
- delay_ms(500); // Force delay to ensure message clears serial write buffer.
-}
-
-// Prints feedback messages. This serves as a centralized method to provide additional
-// user feedback for things that are not of the status/alarm message protocol. These are
-// messages such as setup warnings, switch toggling, and how to exit alarms.
-// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
-// is installed, the message number codes are less than zero.
-// TODO: Install silence feedback messages option in settings
-void report_feedback_message(uint8_t message_code)
-{
- printPgmString(PSTR("["));
- switch(message_code) {
- case MESSAGE_CRITICAL_EVENT:
- printPgmString(PSTR("Reset to continue")); break;
- case MESSAGE_ALARM_LOCK:
- printPgmString(PSTR("'$H'|'$X' to unlock")); break;
- case MESSAGE_ALARM_UNLOCK:
- printPgmString(PSTR("Caution: Unlocked")); break;
- case MESSAGE_ENABLED:
- printPgmString(PSTR("Enabled")); break;
- case MESSAGE_DISABLED:
- printPgmString(PSTR("Disabled")); break;
- }
- printPgmString(PSTR("]\r\n"));
-}
-
-
-// Welcome message
-void report_init_message()
-{
- printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n"));
-}
-
-// Grbl help message
-void report_grbl_help() {
- printPgmString(PSTR("$$ (view Grbl settings)\r\n"
- "$# (view # parameters)\r\n"
- "$G (view parser state)\r\n"
- "$I (view build info)\r\n"
- "$N (view startup blocks)\r\n"
- "$x=value (save Grbl setting)\r\n"
- "$Nx=line (save startup block)\r\n"
- "$C (check gcode mode)\r\n"
- "$X (kill alarm lock)\r\n"
- "$H (run homing cycle)\r\n"
- "~ (cycle start)\r\n"
- "! (feed hold)\r\n"
- "? (current status)\r\n"
- "ctrl-x (reset Grbl)\r\n"));
-}
-
-
-// Grbl global settings print out.
-// NOTE: The numbering scheme here must correlate to storing in settings.c
-void report_grbl_settings() {
- // Print Grbl settings.
- printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds);
- printPgmString(PSTR(" (step pulse, usec)\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time);
- printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
- printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
- printPgmString(PSTR(")\r\n$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 enable invert, bool)\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
- printPgmString(PSTR(" (limit pins invert, bool)\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN));
- printPgmString(PSTR(" (probe pin invert, bool)\r\n$10=")); print_uint8_base10(settings.status_report_mask);
- printPgmString(PSTR(" (status report mask:")); print_uint8_base2(settings.status_report_mask);
- printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation);
- printPgmString(PSTR(" (junction deviation, mm)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance);
- printPgmString(PSTR(" (arc tolerance, mm)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
- printPgmString(PSTR(" (report inches, bool)\r\n$14=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
- printPgmString(PSTR(" (auto start, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
- printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
- printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
- printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask);
- printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
- printPgmString(PSTR(")\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate);
- printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate);
- printPgmString(PSTR(" (homing seek, mm/min)\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay);
- printPgmString(PSTR(" (homing debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
- printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
-
- // Print axis settings
- uint8_t idx, set_idx;
- uint8_t val = AXIS_SETTINGS_START_VAL;
- for (set_idx=0; set_idxline_number;
- }
- printInteger(ln);
- #endif
-
- #ifdef REPORT_REALTIME_RATE
- // Report realtime rate
- printPgmString(PSTR(",F:"));
- printFloat_RateValue(st_get_realtime_rate());
- #endif
-
- printPgmString(PSTR(">\r\n"));
-}
+/*
+ report.c - reporting and messaging methods
+ Part of Grbl
+
+ 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+*/
+
+/*
+ This file functions as the primary feedback interface for Grbl. Any outgoing data, such
+ as the protocol status messages, feedback messages, and status reports, are stored here.
+ For the most part, these functions primarily are called from protocol.c methods. If a
+ different style feedback is desired (i.e. JSON), then a user can change these following
+ methods to accomodate their needs.
+*/
+
+#include "grbl.h"
+
+
+// Internal report utilities to reduce flash with repetitive tasks turned into functions.
+void report_util_setting_prefix(uint8_t n) { serial_write('$'); print_uint8_base10(n); serial_write('='); }
+static void report_util_line_feed() { printPgmString(PSTR("\r\n")); }
+static void report_util_feedback_line_feed() { serial_write(']'); report_util_line_feed(); }
+static void report_util_gcode_modes_G() { printPgmString(PSTR(" G")); }
+static void report_util_gcode_modes_M() { printPgmString(PSTR(" M")); }
+// static void report_util_comment_line_feed() { serial_write(')'); report_util_line_feed(); }
+static void report_util_axis_values(float *axis_value) {
+ uint8_t idx;
+ for (idx=0; idx= AXIS_SETTINGS_INCREMENT) {
+ n -= AXIS_SETTINGS_INCREMENT;
+ idx++;
+ }
+ serial_write(n+'x');
+ switch (idx) {
+ case 0: printPgmString(PSTR(":stp/mm")); break;
+ case 1: printPgmString(PSTR(":mm/min")); break;
+ case 2: printPgmString(PSTR(":mm/s^2")); break;
+ case 3: printPgmString(PSTR(":mm max")); break;
+ }
+ break;
+ }
+ report_util_comment_line_feed();
+}
+*/
+
+static void report_util_uint8_setting(uint8_t n, int val) {
+ report_util_setting_prefix(n);
+ print_uint8_base10(val);
+ report_util_line_feed(); // report_util_setting_string(n);
+}
+static void report_util_float_setting(uint8_t n, float val, uint8_t n_decimal) {
+ report_util_setting_prefix(n);
+ printFloat(val,n_decimal);
+ report_util_line_feed(); // report_util_setting_string(n);
+}
+
+
+// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
+// For every incoming line, this method responds with an 'ok' for a successful command or an
+// 'error:' to indicate some error event with the line or some critical system error during
+// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
+// from a critical error, such as a triggered hard limit. Interface should always monitor for these
+// responses.
+void report_status_message(uint8_t status_code)
+{
+ switch (status_code) {
+ case STATUS_OK: // STATUS_OK
+ printPgmString(PSTR("ok\r\n")); break;
+ default:
+ printPgmString(PSTR("error:"));
+ print_uint8_base10(status_code);
+ report_util_line_feed();
+ }
+}
+
+// Prints alarm messages.
+void report_alarm_message(uint8_t alarm_code)
+{
+ if (alarm_code == EXEC_ALARM_SOFT_LIMIT)
+ {
+ printPgmString(PSTR("Crash Prevented! Pls Reonnect and Re-home"));
+ }
+ else
+ {
+ printPgmString(PSTR("ALARM:"));
+ print_uint8_base10(alarm_code);
+ }
+ report_util_line_feed();
+ delay_ms(500); // Force delay to ensure message clears serial write buffer.
+}
+
+// Prints feedback messages. This serves as a centralized method to provide additional
+// user feedback for things that are not of the status/alarm message protocol. These are
+// messages such as setup warnings, switch toggling, and how to exit alarms.
+// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
+// is installed, the message number codes are less than zero.
+void report_feedback_message(uint8_t message_code)
+{
+ printPgmString(PSTR("[MSG:"));
+ switch(message_code) {
+ case MESSAGE_CRITICAL_EVENT:
+ printPgmString(PSTR("Reset to continue")); break;
+ case MESSAGE_ALARM_LOCK:
+ printPgmString(PSTR("'$H'|'$X' to unlock")); break;
+ case MESSAGE_ALARM_UNLOCK:
+ printPgmString(PSTR("Caution: Unlocked")); break;
+ case MESSAGE_ENABLED:
+ printPgmString(PSTR("Enabled")); break;
+ case MESSAGE_DISABLED:
+ printPgmString(PSTR("Disabled")); break;
+ case MESSAGE_SAFETY_DOOR_AJAR:
+ printPgmString(PSTR("Check Door")); break;
+ case MESSAGE_CHECK_LIMITS:
+ printPgmString(PSTR("Check Limits")); break;
+ case MESSAGE_PROGRAM_END:
+ printPgmString(PSTR("Pgm End")); break;
+ case MESSAGE_RESTORE_DEFAULTS:
+ printPgmString(PSTR("Restoring defaults")); break;
+ case MESSAGE_SPINDLE_RESTORE:
+ printPgmString(PSTR("Restoring spindle")); break;
+ case MESSAGE_SLEEP_MODE:
+ printPgmString(PSTR("Sleeping")); break;
+ }
+ report_util_feedback_line_feed();
+}
+
+
+// Welcome message
+void report_init_message()
+{
+ printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n"));
+}
+
+// Grbl help message
+void report_grbl_help() {
+ printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n"));
+}
+
+
+// Grbl global settings print out.
+// NOTE: The numbering scheme here must correlate to storing in settings.c
+void report_grbl_settings() {
+ // Print Grbl settings.
+ report_util_uint8_setting(0,settings.pulse_microseconds);
+ report_util_uint8_setting(1,settings.stepper_idle_lock_time);
+ report_util_uint8_setting(2,settings.step_invert_mask);
+ report_util_uint8_setting(3,settings.dir_invert_mask);
+ report_util_uint8_setting(4,bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
+ report_util_uint8_setting(5,bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
+ report_util_uint8_setting(6,bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN));
+ report_util_uint8_setting(10,settings.status_report_mask);
+ report_util_float_setting(11,settings.junction_deviation,N_DECIMAL_SETTINGVALUE);
+ report_util_float_setting(12,settings.arc_tolerance,N_DECIMAL_SETTINGVALUE);
+ report_util_uint8_setting(13,bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
+ report_util_uint8_setting(20,bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
+ report_util_uint8_setting(21,bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
+ report_util_uint8_setting(22,bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
+ report_util_uint8_setting(23,settings.homing_dir_mask);
+ report_util_float_setting(24,settings.homing_feed_rate,N_DECIMAL_SETTINGVALUE);
+ report_util_float_setting(25,settings.homing_seek_rate,N_DECIMAL_SETTINGVALUE);
+ report_util_uint8_setting(26,settings.homing_debounce_delay);
+ report_util_float_setting(27,settings.homing_pulloff,N_DECIMAL_SETTINGVALUE);
+ report_util_float_setting(30,settings.rpm_max,N_DECIMAL_RPMVALUE);
+ report_util_float_setting(31,settings.rpm_min,N_DECIMAL_RPMVALUE);
+ #ifdef VARIABLE_SPINDLE
+ report_util_uint8_setting(32,bit_istrue(settings.flags,BITFLAG_LASER_MODE));
+ #else
+ report_util_uint8_setting(32,0);
+ #endif
+ // Print axis settings
+ uint8_t idx, set_idx;
+ uint8_t val = AXIS_SETTINGS_START_VAL;
+ for (set_idx=0; set_idx= MOTION_MODE_PROBE_TOWARD) {
+ printPgmString(PSTR("38."));
+ print_uint8_base10(gc_state.modal.motion - (MOTION_MODE_PROBE_TOWARD-2));
+ } else {
+ print_uint8_base10(gc_state.modal.motion);
+ }
+
+ report_util_gcode_modes_G();
+ print_uint8_base10(gc_state.modal.coord_select+54);
+
+ report_util_gcode_modes_G();
+ print_uint8_base10(gc_state.modal.plane_select+17);
+
+ report_util_gcode_modes_G();
+ print_uint8_base10(21-gc_state.modal.units);
+
+ report_util_gcode_modes_G();
+ print_uint8_base10(gc_state.modal.distance+90);
+
+ report_util_gcode_modes_G();
+ print_uint8_base10(94-gc_state.modal.feed_rate);
+
+ if (gc_state.modal.program_flow) {
+ report_util_gcode_modes_M();
+ switch (gc_state.modal.program_flow) {
+ case PROGRAM_FLOW_PAUSED : serial_write('0'); break;
+ // case PROGRAM_FLOW_OPTIONAL_STOP : serial_write('1'); break; // M1 is ignored and not supported.
+ case PROGRAM_FLOW_COMPLETED_M2 :
+ case PROGRAM_FLOW_COMPLETED_M30 :
+ print_uint8_base10(gc_state.modal.program_flow);
+ break;
+ }
+ }
+
+ report_util_gcode_modes_M();
+ switch (gc_state.modal.spindle) {
+ case SPINDLE_ENABLE_CW : serial_write('3'); break;
+ case SPINDLE_ENABLE_CCW : serial_write('4'); break;
+ case SPINDLE_DISABLE : serial_write('5'); break;
+ }
+
+ #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) { report_util_gcode_modes_M(); serial_write('7'); }
+ if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_FLOOD) { report_util_gcode_modes_M(); serial_write('8'); }
+ } else { report_util_gcode_modes_M(); serial_write('9'); }
+ #else
+ report_util_gcode_modes_M();
+ if (gc_state.modal.coolant) { serial_write('8'); }
+ else { serial_write('9'); }
+ #endif
+
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ if (sys.override_ctrl == OVERRIDE_PARKING_MOTION) {
+ report_util_gcode_modes_M();
+ print_uint8_base10(56);
+ }
+ #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(gc_state.spindle_speed,N_DECIMAL_RPMVALUE);
+ #endif
+
+ report_util_feedback_line_feed();
+}
+
+// Prints specified startup line
+void report_startup_line(uint8_t n, char *line)
+{
+ printPgmString(PSTR("$N"));
+ print_uint8_base10(n);
+ serial_write('=');
+ printString(line);
+ report_util_line_feed();
+}
+
+void report_execute_startup_message(char *line, uint8_t status_code)
+{
+ serial_write('>');
+ printString(line);
+ serial_write(':');
+ report_status_message(status_code);
+}
+
+// Prints build info line
+void report_build_info(char *line)
+{
+ printPgmString(PSTR("[VER:" GRBL_VERSION "." GRBL_VERSION_BUILD ":"));
+ printString(line);
+ report_util_feedback_line_feed();
+ printPgmString(PSTR("[OPT:")); // Generate compile-time build option list
+ #ifdef VARIABLE_SPINDLE
+ serial_write('V');
+ #endif
+ #ifdef USE_LINE_NUMBERS
+ serial_write('N');
+ #endif
+ #ifdef ENABLE_M7
+ serial_write('M');
+ #endif
+ #ifdef COREXY
+ serial_write('C');
+ #endif
+ #ifdef PARKING_ENABLE
+ serial_write('P');
+ #endif
+ #ifdef HOMING_FORCE_SET_ORIGIN
+ serial_write('Z');
+ #endif
+ #ifdef HOMING_SINGLE_AXIS_COMMANDS
+ serial_write('H');
+ #endif
+ #ifdef LIMITS_TWO_SWITCHES_ON_AXES
+ serial_write('T');
+ #endif
+ #ifdef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES
+ serial_write('A');
+ #endif
+ #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
+ serial_write('D');
+ #endif
+ #ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED
+ serial_write('0');
+ #endif
+ #ifdef ENABLE_SOFTWARE_DEBOUNCE
+ serial_write('S');
+ #endif
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ serial_write('R');
+ #endif
+ #ifndef HOMING_INIT_LOCK
+ serial_write('L');
+ #endif
+ #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
+ serial_write('+');
+ #endif
+ #ifndef ENABLE_RESTORE_EEPROM_WIPE_ALL // NOTE: Shown when disabled.
+ serial_write('*');
+ #endif
+ #ifndef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS // NOTE: Shown when disabled.
+ serial_write('$');
+ #endif
+ #ifndef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS // NOTE: Shown when disabled.
+ serial_write('#');
+ #endif
+ #ifndef ENABLE_BUILD_INFO_WRITE_COMMAND // NOTE: Shown when disabled.
+ serial_write('I');
+ #endif
+ #ifndef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE // NOTE: Shown when disabled.
+ serial_write('E');
+ #endif
+ #ifndef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // NOTE: Shown when disabled.
+ serial_write('W');
+ #endif
+ #ifdef ENABLE_DUAL_AXIS
+ serial_write('2');
+ #endif
+ // NOTE: Compiled values, like override increments/max/min values, may be added at some point later.
+ serial_write(',');
+ print_uint8_base10(BLOCK_BUFFER_SIZE-1);
+ serial_write(',');
+ print_uint8_base10(RX_BUFFER_SIZE);
+
+ report_util_feedback_line_feed();
+}
+
+
+// Prints the character string line Grbl has received from the user, which has been pre-parsed,
+// and has been sent into protocol_execute_line() routine to be executed by Grbl.
+void report_echo_line_received(char *line)
+{
+ printPgmString(PSTR("[echo: ")); printString(line);
+ report_util_feedback_line_feed();
+}
+
+
+ // 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,
+ // especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
+void report_realtime_status()
+{
+ 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
+ serial_write('<');
+ 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")); break;
+ }
+
+ float wco[N_AXIS];
+ if (bit_isfalse(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE) ||
+ (sys.report_wco_counter == 0) ) {
+ for (idx=0; idx< N_AXIS; idx++) {
+ // Apply work coordinate offsets and tool length offset to current position.
+ wco[idx] = gc_state.coord_system[idx]+gc_state.coord_offset[idx];
+ if (idx == TOOL_LENGTH_OFFSET_AXIS) { wco[idx] += gc_state.tool_length_offset; }
+ if (bit_isfalse(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
+ print_position[idx] -= wco[idx];
+ }
+ }
+ }
+
+ // Report machine position
+ if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
+ printPgmString(PSTR("|MPos:"));
+ } else {
+ printPgmString(PSTR("|WPos:"));
+ }
+ report_util_axis_values(print_position);
+
+ // Returns planner and serial read buffer states.
+ #ifdef REPORT_FIELD_BUFFER_STATE
+ if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_BUFFER_STATE)) {
+ printPgmString(PSTR("|Bf:"));
+ print_uint8_base10(plan_get_block_buffer_available());
+ serial_write(',');
+ print_uint8_base10(serial_get_rx_buffer_available());
+ }
+ #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 feed speed
+ #ifdef REPORT_FIELD_CURRENT_FEED_SPEED
+ #ifdef VARIABLE_SPINDLE
+ printPgmString(PSTR("|FS:"));
+ printFloat_RateValue(st_get_realtime_rate());
+ serial_write(',');
+ printFloat(sys.spindle_speed,N_DECIMAL_RPMVALUE);
+ #else
+ printPgmString(PSTR("|F:"));
+ printFloat_RateValue(st_get_realtime_rate());
+ #endif
+ #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) {
+ #ifdef ENABLE_DUAL_AXIS
+ #if (DUAL_AXIS_SELECT == X_AXIS)
+ if (bit_istrue(lim_pin_state,(bit(X_AXIS)|bit(N_AXIS)))) { serial_write('X'); }
+ if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); }
+ #endif
+ #if (DUAL_AXIS_SELECT == Y_AXIS)
+ if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); }
+ if (bit_istrue(lim_pin_state,(bit(Y_AXIS)|bit(N_AXIS)))) { serial_write('Y'); }
+ #endif
+ if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); }
+ #else
+ 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'); }
+ #endif
+ }
+ 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 > 0) { sys.report_wco_counter--; }
+ else {
+ if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
+ sys.report_wco_counter = (REPORT_WCO_REFRESH_BUSY_COUNT-1); // Reset counter for slow refresh
+ } else { sys.report_wco_counter = (REPORT_WCO_REFRESH_IDLE_COUNT-1); }
+ if (sys.report_ovr_counter == 0) { sys.report_ovr_counter = 1; } // Set override on next report.
+ printPgmString(PSTR("|WCO:"));
+ report_util_axis_values(wco);
+ }
+ #endif
+
+ #ifdef REPORT_FIELD_OVERRIDES
+ if (sys.report_ovr_counter > 0) { sys.report_ovr_counter--; }
+ else {
+ if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
+ sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-1); // Reset counter for slow refresh
+ } else { sys.report_ovr_counter = (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);
+
+ uint8_t sp_state = spindle_get_state();
+ uint8_t cl_state = coolant_get_state();
+ if (sp_state || cl_state) {
+ printPgmString(PSTR("|A:"));
+ if (sp_state) { // != SPINDLE_STATE_DISABLE
+ #ifdef VARIABLE_SPINDLE
+ #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
+ serial_write('S'); // CW
+ #else
+ if (sp_state == SPINDLE_STATE_CW) { serial_write('S'); } // CW
+ else { serial_write('C'); } // CCW
+ #endif
+ #else
+ if (sp_state & SPINDLE_STATE_CW) { serial_write('S'); } // CW
+ else { serial_write('C'); } // CCW
+ #endif
+ }
+ if (cl_state & COOLANT_STATE_FLOOD) { serial_write('F'); }
+ #ifdef ENABLE_M7
+ if (cl_state & COOLANT_STATE_MIST) { serial_write('M'); }
+ #endif
+ }
+ }
+ #endif
+
+ serial_write('>');
+ report_util_line_feed();
+}
+
+
+#ifdef DEBUG
+ void report_realtime_debug()
+ {
+
+ }
+#endif
+
+
+#ifdef M114M115
+// handles M115
+void report_current_position()
+{
+ uint8_t idx;
+ int32_t current_position[N_AXIS]; // Copy current state of the system position variable
+ float print_position[N_AXIS];
+ float wco[N_AXIS];
+
+ memcpy(current_position, sys_position, sizeof(sys_position));
+ system_convert_array_steps_to_mpos(print_position, current_position);
+
+ //serial_write('<');
+ //printPgmString(PSTR("ok"));
+ //report_util_line_feed();
+
+ for (idx = 0; idx < N_AXIS; idx++)
+ {
+ wco[idx] = gc_state.coord_system[idx] + gc_state.coord_offset[idx];
+ print_position[idx] -= wco[idx];
+ }
+
+ //printPgmString(PSTR("WPos:"));
+ report_util_axis_values(print_position);
+ //serial_write('>');
+ report_util_line_feed();
+}
+
+// handles M114
+void report_firmware()
+{
+ // M115: Get Firmware Version and Capabilities, see https://www.reprap.org/wiki/G-code#M115:_Get_Firmware_Version_and_Capabilities.
+ printPgmString(PSTR("FIRMWARE_NAME:Grbl, FIRMWARE_URL:https://github.com/openpnp/grbl, FIRMWARE_VERSION: " GRBL_VERSION "\r\n"));
+}
+
+#endif // M114M115
+
diff --git a/report.h b/report.h
index ffbdcc331..bc785a752 100644
--- a/report.h
+++ b/report.h
@@ -1,8 +1,8 @@
/*
report.h - reporting and messaging methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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,9 +30,15 @@
#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_SETTING_DISABLED_LASER 17
#define STATUS_GCODE_UNSUPPORTED_COMMAND 20
#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21
@@ -52,24 +58,37 @@
#define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35
#define STATUS_GCODE_UNUSED_WORDS 36
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37
-
-// Define Grbl alarm codes. Less than zero to distinguish alarm error from status error.
-#define ALARM_LIMIT_ERROR -1
-#define ALARM_ABORT_CYCLE -2
-#define ALARM_PROBE_FAIL -3
-
-// Define Grbl feedback message codes.
+#define STATUS_GCODE_MAX_VALUE_EXCEEDED 38
+
+// 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. 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_CHECK_LIMITS 7
+#define MESSAGE_PROGRAM_END 8
+#define MESSAGE_RESTORE_DEFAULTS 9
+#define MESSAGE_SPINDLE_RESTORE 10
+#define MESSAGE_SLEEP_MODE 11
// Prints system status messages.
void report_status_message(uint8_t status_code);
// Prints system alarm messages.
-void report_alarm_message(int8_t alarm_code);
+void report_alarm_message(uint8_t alarm_code);
// Prints miscellaneous feedback messages.
void report_feedback_message(uint8_t message_code);
@@ -83,6 +102,9 @@ void report_grbl_help();
// Prints Grbl global settings
void report_grbl_settings();
+// Prints an echo of the pre-parsed line received right before execution.
+void report_echo_line_received(char *line);
+
// Prints realtime status report
void report_realtime_status();
@@ -95,10 +117,20 @@ void report_ngc_parameters();
// Prints current g-code parser mode state
void report_gcode_modes();
-// Prints startup line
+// Prints startup line when requested and executed.
void report_startup_line(uint8_t n, char *line);
+void report_execute_startup_message(char *line, uint8_t status_code);
// Prints build info and user info
void report_build_info(char *line);
+#ifdef DEBUG
+ void report_realtime_debug();
+#endif
+
+#ifdef M114M115
+void report_current_position(); // M114
+void report_firmware(); // M115
+#endif //
+
#endif
diff --git a/serial.c b/serial.c
index 8e7d50b9d..cf5f35e2e 100644
--- a/serial.c
+++ b/serial.c
@@ -1,8 +1,9 @@
/*
serial.c - Low level functions for sending and recieving bytes via the serial port
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,35 +18,32 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-#include
-#include "system.h"
-#include "serial.h"
-#include "motion_control.h"
-#include "protocol.h"
+#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;
-#ifdef ENABLE_XONXOFF
- volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
-#endif
-
+// Returns the number of bytes available in the RX serial buffer.
+uint8_t serial_get_rx_buffer_available()
+{
+ uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
+ if (serial_rx_buffer_head >= rtail) { return(RX_BUFFER_SIZE - (serial_rx_buffer_head-rtail)); }
+ return((rtail-serial_rx_buffer_head-1));
+}
+
// Returns the number of bytes used in the RX serial buffer.
+// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h.
uint8_t serial_get_rx_buffer_count()
{
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
@@ -60,7 +58,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));
}
@@ -76,37 +74,32 @@ void serial_init()
#endif
UBRR0H = UBRR0_value >> 8;
UBRR0L = UBRR0_value;
-
- // enable rx and tx
- UCSR0B |= 1<= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
- flow_ctrl = SEND_XOFF;
- UCSR0B |= (1 << UDRIE0); // Force TX
- }
- #endif
-
+ 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
+ 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
+ case CMD_JOG_CANCEL:
+ if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
+ system_set_exec_state_flag(EXEC_MOTION_CANCEL);
+ }
+ break;
+ #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;
+ }
}
- //TODO: else alarm on overflow?
}
}
-void serial_reset_read_buffer()
+void serial_reset_read_buffer()
{
serial_rx_buffer_tail = serial_rx_buffer_head;
-
- #ifdef ENABLE_XONXOFF
- flow_ctrl = XON_SENT;
- #endif
}
diff --git a/serial.h b/serial.h
index c281949c5..5a3f7761b 100644
--- a/serial.h
+++ b/serial.h
@@ -1,8 +1,9 @@
/*
serial.c - Low level functions for sending and recieving bytes via the serial port
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,12 +18,6 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef serial_h
#define serial_h
@@ -32,21 +27,15 @@
#define RX_BUFFER_SIZE 128
#endif
#ifndef TX_BUFFER_SIZE
- #define TX_BUFFER_SIZE 64
+ #ifdef USE_LINE_NUMBERS
+ #define TX_BUFFER_SIZE 112
+ #else
+ #define TX_BUFFER_SIZE 104
+ #endif
#endif
#define SERIAL_NO_DATA 0xff
-#ifdef ENABLE_XONXOFF
- #define RX_BUFFER_FULL 96 // XOFF high watermark
- #define RX_BUFFER_LOW 64 // XON low watermark
- #define SEND_XOFF 1
- #define SEND_XON 2
- #define XOFF_SENT 3
- #define XON_SENT 4
- #define XOFF_CHAR 0x13
- #define XON_CHAR 0x11
-#endif
void serial_init();
@@ -59,7 +48,11 @@ uint8_t serial_read();
// Reset and empty data in read buffer. Used by e-stop and reset.
void serial_reset_read_buffer();
+// Returns the number of bytes available in the RX serial buffer.
+uint8_t serial_get_rx_buffer_available();
+
// Returns the number of bytes used in the RX serial buffer.
+// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h.
uint8_t serial_get_rx_buffer_count();
// Returns the number of bytes used in the TX serial buffer.
diff --git a/settings.c b/settings.c
index ed15690e7..ace87412b 100644
--- a/settings.c
+++ b/settings.c
@@ -1,9 +1,9 @@
/*
- settings.c - eeprom configuration handling
- Part of Grbl v0.9
+ settings.c - eeprom configuration handling
+ Part of Grbl
- Copyright (c) 2012-2014 Sungeun K. Jeon
- Copyright (c) 2014 Bob Beattie
+ 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
@@ -18,124 +18,131 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "settings.h"
-#include "eeprom.h"
-#include "protocol.h"
-#include "report.h"
-#include "limits.h"
-#include "stepper.h"
+
+#include "grbl.h"
settings_t settings;
+const __flash settings_t defaults = {\
+ .pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS,
+ .stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME,
+ .step_invert_mask = DEFAULT_STEPPING_INVERT_MASK,
+ .dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK,
+ .status_report_mask = DEFAULT_STATUS_REPORT_MASK,
+ .junction_deviation = DEFAULT_JUNCTION_DEVIATION,
+ .arc_tolerance = DEFAULT_ARC_TOLERANCE,
+ .rpm_max = DEFAULT_SPINDLE_RPM_MAX,
+ .rpm_min = DEFAULT_SPINDLE_RPM_MIN,
+ .homing_dir_mask = DEFAULT_HOMING_DIR_MASK,
+ .homing_feed_rate = DEFAULT_HOMING_FEED_RATE,
+ .homing_seek_rate = DEFAULT_HOMING_SEEK_RATE,
+ .homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY,
+ .homing_pulloff = DEFAULT_HOMING_PULLOFF,
+ .flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | \
+ (DEFAULT_LASER_MODE << BIT_LASER_MODE) | \
+ (DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | \
+ (DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) | \
+ (DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | \
+ (DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) | \
+ (DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | \
+ (DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN),
+ .steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM,
+ .steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM,
+ .steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM,
+ .steps_per_mm[C_AXIS] = DEFAULT_C_STEPS_PER_MM,
+ .max_rate[X_AXIS] = DEFAULT_X_MAX_RATE,
+ .max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE,
+ .max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE,
+ .max_rate[C_AXIS] = DEFAULT_C_MAX_RATE,
+ .acceleration[X_AXIS] = DEFAULT_X_ACCELERATION,
+ .acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION,
+ .acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION,
+ .acceleration[C_AXIS] = DEFAULT_C_ACCELERATION,
+ .max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL),
+ .max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL),
+ .max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL),
+ .max_travel[C_AXIS] = (-DEFAULT_C_MAX_TRAVEL)};
+
// Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char *line)
{
+ #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
+ 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()
{
+#ifdef VISUAL_HOMING
+ uint8_t flags = settings.flags;
+ settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // do want to mess it up!
+#endif
eeprom_put_char(0, SETTINGS_VERSION);
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
+#ifdef VISUAL_HOMING
+ settings.flags = flags; // restore
+#endif
}
-// Method to restore EEPROM-saved Grbl global settings back to defaults.
-void settings_restore_global_settings() {
- 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.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
- settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
- settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
- settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
- settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
-
- settings.flags = 0;
- if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
- if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
- 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.steps_per_mm[C_AXIS] = DEFAULT_C_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.max_rate[C_AXIS] = DEFAULT_C_MAX_RATE;
- settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
- settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
- settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
- settings.acceleration[C_AXIS] = DEFAULT_C_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.max_travel[C_AXIS] = (-DEFAULT_C_MAX_TRAVEL);
-
- write_global_settings();
-}
-
+// 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 = defaults;
+ write_global_settings();
+ }
-// Helper function to clear the EEPROM space containing parameter data.
-void settings_clear_parameters() {
- uint8_t idx;
- float coord_data[3];
- 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_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); }
+ }
+ if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) {
+ #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
+ }
-// Helper function to clear the EEPROM space containing the startup lines.
-void settings_clear_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 (restore_flag & SETTINGS_RESTORE_BUILD_INFO) {
+ eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0);
+ eeprom_put_char(EEPROM_ADDR_BUILD_INFO+1 , 0); // Checksum
+ }
}
-// Helper function to clear the EEPROM space containing the user build info string.
-void settings_clear_build_info() { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); }
-
-
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_startup_line(uint8_t n, char *line)
{
@@ -169,12 +176,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.
@@ -187,7 +194,7 @@ uint8_t read_global_settings() {
return(false);
}
} else {
- return(false);
+ return(false);
}
return(true);
}
@@ -195,7 +202,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.
@@ -205,8 +212,18 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (parameter < N_AXIS) {
// Valid axis setting found.
switch (set_idx) {
- case 0: settings.steps_per_mm[parameter] = value; break;
- case 1: settings.max_rate[parameter] = value; break;
+ case 0:
+ #ifdef MAX_STEP_RATE_HZ
+ if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
+ #endif
+ settings.steps_per_mm[parameter] = value;
+ break;
+ case 1:
+ #ifdef MAX_STEP_RATE_HZ
+ if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
+ #endif
+ settings.max_rate[parameter] = value;
+ break;
case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
}
@@ -222,16 +239,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.
@@ -245,6 +262,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;
@@ -252,15 +270,12 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 13:
if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; }
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
- break;
- case 14: // Reset to ensure change. Immediate re-init may cause problems.
- if (int_value) { settings.flags |= BITFLAG_AUTO_START; }
- else { settings.flags &= ~BITFLAG_AUTO_START; }
+ system_flag_wco_change(); // Make sure WCO is immediately updated.
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:
@@ -270,8 +285,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;
@@ -280,7 +295,17 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
- default:
+ case 30: settings.rpm_max = value; spindle_init(); break; // Re-initialize spindle rpm calibration
+ case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration
+ case 32:
+ #ifdef VARIABLE_SPINDLE
+ if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }
+ else { settings.flags &= ~BITFLAG_LASER_MODE; }
+ #else
+ return(STATUS_SETTING_DISABLED_LASER);
+ #endif
+ break;
+ default:
return(STATUS_INVALID_STATEMENT);
}
}
@@ -293,27 +318,9 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
void settings_init() {
if(!read_global_settings()) {
report_status_message(STATUS_SETTING_READ_FAIL);
-
- settings_restore_global_settings();
-
- // Force clear startup lines and build info user data. Parameters should be ok.
- // TODO: For next version, remove these clears. Only here because line buffer increased.
- settings_clear_startup_lines();
- settings_clear_build_info();
-
+ settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data.
report_grbl_settings();
}
-
- // 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.
- // TODO: Build info should be checked here, but will wait until v1.0 to address this. Ok for now.
}
@@ -333,7 +340,7 @@ uint8_t get_direction_pin_mask(uint8_t axis_idx)
if ( axis_idx == X_AXIS ) { return((1<.
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
#ifndef settings_h
#define settings_h
+#include "grbl.h"
-#define GRBL_VERSION "0.9g"
-#define GRBL_VERSION_BUILD "20140905"
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
// when firmware is upgraded. Always stored in byte 0 of eeprom
-#define SETTINGS_VERSION 9 // NOTE: Check settings_reset() when moving to next version.
+#define SETTINGS_VERSION 10 // NOTE: Check settings_reset() when moving to next version.
// Define bit flag masks for the boolean settings in settings.flag.
-#define BITFLAG_REPORT_INCHES bit(0)
-#define BITFLAG_AUTO_START bit(1)
-#define BITFLAG_INVERT_ST_ENABLE bit(2)
-#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
-#define BITFLAG_HOMING_ENABLE bit(4)
-#define BITFLAG_SOFT_LIMIT_ENABLE bit(5)
-#define BITFLAG_INVERT_LIMIT_PINS bit(6)
-#define BITFLAG_INVERT_PROBE_PIN bit(7)
+#define BIT_REPORT_INCHES 0
+#define BIT_LASER_MODE 1
+#define BIT_INVERT_ST_ENABLE 2
+#define BIT_HARD_LIMIT_ENABLE 3
+#define BIT_HOMING_ENABLE 4
+#define BIT_SOFT_LIMIT_ENABLE 5
+#define BIT_INVERT_LIMIT_PINS 6
+#define BIT_INVERT_PROBE_PIN 7
+
+#define BITFLAG_REPORT_INCHES bit(BIT_REPORT_INCHES)
+#define BITFLAG_LASER_MODE bit(BIT_LASER_MODE)
+#define BITFLAG_INVERT_ST_ENABLE bit(BIT_INVERT_ST_ENABLE)
+#define BITFLAG_HARD_LIMIT_ENABLE bit(BIT_HARD_LIMIT_ENABLE)
+#define BITFLAG_HOMING_ENABLE bit(BIT_HOMING_ENABLE)
+#define BITFLAG_SOFT_LIMIT_ENABLE bit(BIT_SOFT_LIMIT_ENABLE)
+#define BITFLAG_INVERT_LIMIT_PINS bit(BIT_INVERT_LIMIT_PINS)
+#define BITFLAG_INVERT_PROBE_PIN bit(BIT_INVERT_PROBE_PIN)
// 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_POSITION_TYPE bit(0)
+#define BITFLAG_RT_STATUS_BUFFER_STATE bit(1)
+
+// Define settings restore 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
@@ -89,7 +99,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;
@@ -103,11 +116,8 @@ extern settings_t settings;
// Initialize the configuration subsystem (load settings from EEPROM)
void settings_init();
-// Helper functions to clear and restore EEPROM defaults
-void settings_restore_global_settings();
-void settings_clear_parameters();
-void settings_clear_startup_line();
-void settings_clear_build_info();
+// Helper function to clear and restore EEPROM defaults
+void settings_restore(uint8_t restore_flag);
// A helper method to set new settings from command line
uint8_t settings_store_global_setting(uint8_t parameter, float value);
diff --git a/spindle_control.c b/spindle_control.c
index b8410844e..550b75200 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -1,8 +1,9 @@
/*
spindle_control.c - spindle control methods
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 Sungeun K. Jeon
+ Copyright (c) 2012-2017 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
@@ -17,86 +18,273 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012 Sungeun K. Jeon
-*/
-#include "system.h"
-#include "spindle_control.h"
-#include "protocol.h"
-#include "gcode.h"
+#include "grbl.h"
+
+
+#ifdef VARIABLE_SPINDLE
+ static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions.
+#endif
void spindle_init()
-{
- // On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
+{
#ifdef VARIABLE_SPINDLE
+ // Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
+ // combined unless configured otherwise.
SPINDLE_PWM_DDR |= (1<= settings.rpm_max) || (rpm >= RPM_MAX)) {
+ rpm = RPM_MAX;
+ pwm_value = SPINDLE_PWM_MAX_VALUE;
+ } else if (rpm <= RPM_MIN) {
+ if (rpm == 0.0) { // S0 disables spindle
+ pwm_value = SPINDLE_PWM_OFF_VALUE;
+ } else {
+ rpm = RPM_MIN;
+ pwm_value = SPINDLE_PWM_MIN_VALUE;
+ }
+ } else {
+ // Compute intermediate PWM value with linear spindle speed model via piecewise linear fit model.
+ #if (N_PIECES > 3)
+ if (rpm > RPM_POINT34) {
+ pwm_value = floor(RPM_LINE_A4*rpm - RPM_LINE_B4);
+ } else
+ #endif
+ #if (N_PIECES > 2)
+ if (rpm > RPM_POINT23) {
+ pwm_value = floor(RPM_LINE_A3*rpm - RPM_LINE_B3);
+ } else
+ #endif
+ #if (N_PIECES > 1)
+ if (rpm > RPM_POINT12) {
+ pwm_value = floor(RPM_LINE_A2*rpm - RPM_LINE_B2);
+ } else
+ #endif
+ {
+ pwm_value = floor(RPM_LINE_A1*rpm - RPM_LINE_B1);
+ }
+ }
+ sys.spindle_speed = rpm;
+ return(pwm_value);
+ }
+
+ #else
+
+ // Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
+ uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit.
+ {
+ uint8_t pwm_value;
+ rpm *= (0.010*sys.spindle_speed_ovr); // Scale by spindle speed override value.
+ // 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.
+ sys.spindle_speed = settings.rpm_max;
+ pwm_value = SPINDLE_PWM_MAX_VALUE;
+ } else if (rpm <= settings.rpm_min) {
+ if (rpm == 0.0) { // S0 disables spindle
+ sys.spindle_speed = 0.0;
+ pwm_value = SPINDLE_PWM_OFF_VALUE;
+ } else { // Set minimum PWM output
+ sys.spindle_speed = settings.rpm_min;
+ pwm_value = SPINDLE_PWM_MIN_VALUE;
+ }
+ } else {
+ // Compute intermediate PWM value with linear spindle speed model.
+ // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight.
+ sys.spindle_speed = rpm;
+ pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE;
+ }
+ return(pwm_value);
}
+
+ #endif
+#endif
+
+// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
+// Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
+// sleep, and spindle stop override.
+#ifdef VARIABLE_SPINDLE
+ void spindle_set_state(uint8_t state, float rpm)
+#else
+ void _spindle_set_state(uint8_t state)
+#endif
+{
+ if (sys.abort) { return; } // Block during abort.
+
+ if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm.
+
#ifdef VARIABLE_SPINDLE
- // TODO: Install the optional capability for frequency-based output for servos.
- #define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
- TCCRA_REGISTER = (1< SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent uint8 overflow
- uint8_t current_pwm = floor( rpm*(255.0/SPINDLE_RPM_RANGE) + 0.5);
- OCR_REGISTER = current_pwm;
+ sys.spindle_speed = 0.0;
+ #endif
+ spindle_stop();
+
+ } else {
- #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared.
+ #if !defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(ENABLE_DUAL_AXIS)
+ if (state == SPINDLE_ENABLE_CW) {
+ SPINDLE_DIRECTION_PORT &= ~(1<.
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2012 Sungeun K. Jeon
-*/
#ifndef spindle_control_h
-#define spindle_control_h
+#define spindle_control_h
+
+#define SPINDLE_NO_SYNC false
+#define SPINDLE_FORCE_SYNC true
+
+#define SPINDLE_STATE_DISABLE 0 // Must be zero.
+#define SPINDLE_STATE_CW bit(0)
+#define SPINDLE_STATE_CCW bit(1)
// Initializes spindle pins and hardware PWM, if enabled.
void spindle_init();
-// Sets spindle direction and spindle rpm via PWM, if enabled.
-void spindle_run(uint8_t direction, float rpm);
+// Returns current spindle output state. Overrides may alter it from programmed states.
+uint8_t spindle_get_state();
+
+// Called by g-code parser when setting spindle state and requires a buffer sync.
+// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
+// Called by spindle_sync() after sync and parking motion/spindle stop override during restore.
+#ifdef VARIABLE_SPINDLE
+
+ // Called by g-code parser when setting spindle state and requires a buffer sync.
+ void spindle_sync(uint8_t state, float rpm);
+
+ // Sets spindle running state with direction, enable, and spindle PWM.
+ void spindle_set_state(uint8_t state, float rpm);
+
+ // Sets spindle PWM quickly for stepper ISR. Also called by spindle_set_state().
+ // NOTE: 328p PWM register is 8-bit.
+ void spindle_set_speed(uint8_t pwm_value);
+
+ // Computes 328p-specific PWM register value for the given RPM for quick updating.
+ uint8_t spindle_compute_pwm_value(float rpm);
+
+#else
+
+ // Called by g-code parser when setting spindle state and requires a buffer sync.
+ #define spindle_sync(state, rpm) _spindle_sync(state)
+ void _spindle_sync(uint8_t state);
-// Kills spindle.
+ // Sets spindle running state with direction and enable.
+ #define spindle_set_state(state, rpm) _spindle_set_state(state)
+ void _spindle_set_state(uint8_t state);
+
+#endif
+
+// Stop and start spindle routines. Called by all spindle routines and stepper ISR.
void spindle_stop();
+
#endif
diff --git a/stepper.c b/stepper.c
index 65209960c..bcebc9bd5 100644
--- a/stepper.c
+++ b/stepper.c
@@ -1,878 +1,1047 @@
-/*
- stepper.c - stepper motor driver: executes motion plans using stepper motors
- Part of Grbl v0.9
-
- Copyright (c) 2012-2014 Sungeun K. Jeon
- Copyright (c) 2014 Bob Beattie
-
- Grbl is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Grbl is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Grbl. If not, see .
-*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011-2012 Sungeun K. Jeon
-*/
-
-#include "system.h"
-#include "nuts_bolts.h"
-#include "stepper.h"
-#include "settings.h"
-#include "planner.h"
-#include "probe.h"
-
-
-// Some useful constants.
-#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 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
-// 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
-// and timer accuracy. Do not alter these settings unless you know what you are doing.
-#define MAX_AMASS_LEVEL 3
-// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
-#define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
-#define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4)
-#define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)
-
-
-// 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 {
- uint8_t direction_bits;
- 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
-// 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
-// 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
- uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
- uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
- #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
- uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
- #else
- uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
- #endif
-} segment_t;
-static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
-
-// Stepper ISR data struct. Contains the running data for the main stepper ISR.
-typedef struct {
- // Used by the bresenham line algorithm
- uint32_t counter_x, // Counter variables for the bresenham line tracer
- counter_y,
- counter_z,
- counter_c; // Additional axis.
- #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
- uint8_t dir_outbits;
- #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
- uint32_t steps[N_AXIS];
- #endif
-
- 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
-} stepper_t;
-static stepper_t st;
-
-// Step segment ring buffer indices
-static volatile uint8_t segment_buffer_tail;
-static uint8_t segment_buffer_head;
-static uint8_t segment_next_head;
-
-// Step and direction port invert masks.
-static uint8_t step_port_invert_mask;
-static uint8_t dir_port_invert_mask;
-
-// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
-static volatile uint8_t busy;
-
-// 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
-
-// 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 flag_partial_block; // Flag indicating the last block completed. Time to load a new one.
-
- float steps_remaining;
- float step_per_mm; // Current planner block step/millimeter conversion scalar
- float req_mm_increment;
- float dt_remainder;
-
- uint8_t ramp_type; // Current segment ramp state
- float mm_complete; // End of velocity profile from end of current planner block in (mm).
- // NOTE: This value must coincide with a step(no mantissa) when converted.
- float current_speed; // Current speed at the end of the segment buffer (mm/min)
- float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min)
- float exit_speed; // Exit speed of executing block (mm/min)
- float accelerate_until; // Acceleration ramp end measured from end of block (mm)
- float decelerate_after; // Deceleration ramp start measured from end of block (mm)
-} st_prep_t;
-static st_prep_t prep;
-
-
-/* BLOCK VELOCITY PROFILE DEFINITION
- __________________________
- /| |\ _________________ ^
- / | | \ /| |\ |
- / | | \ / | | \ s
- / | | | | | \ p
- / | | | | | \ e
- +-----+------------------------+---+--+---------------+----+ e
- | 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
- stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise-
- deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
- triangle(no cruise).
-
- maximum_speed (< nominal_speed) -> +
- +--------+ <- maximum_speed (= nominal_speed) /|\
- / \ / | \
- current_speed -> + \ / | + <- exit_speed
- | + <- 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
- 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()
-{
- // Enable stepper drivers.
- if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<> 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<prescaler<cycles_per_tick;
- st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
- // If the new segment starts a new planner block, initialize stepper variables and counters.
- // NOTE: When the segment data index changes, this indicates a new planner block.
- 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.exec_block->step_event_count >> 1);
- st.counter_y = st.counter_x;
- st.counter_z = st.counter_x;
- st.counter_c = st.counter_x;
- }
-
- 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.
- st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
- 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;
- st.steps[C_AXIS] = st.exec_block->steps[C_AXIS] >> st.exec_segment->amass_level;
- #endif
-
- } else {
- // Segment buffer empty. Shutdown.
- st_go_idle();
- bit_true_atomic(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
- return; // Nothing to do but exit.
- }
- }
-
-
- // Check probing state.
- probe_state_monitor();
-
- // Reset step out bits.
- 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
- if (st.counter_x > st.exec_block->step_event_count) {
- st.step_outbits |= (1<step_event_count;
- if (st.exec_block->direction_bits & (1<steps[Y_AXIS];
- #endif
- if (st.counter_y > st.exec_block->step_event_count) {
- st.step_outbits |= (1<step_event_count;
- if (st.exec_block->direction_bits & (1<steps[Z_AXIS];
- #endif
- if (st.counter_z > st.exec_block->step_event_count) {
- st.step_outbits |= (1<step_event_count;
- if (st.exec_block->direction_bits & (1<steps[C_AXIS];
- #endif
- if (st.counter_c > st.exec_block->step_event_count) {
- st.step_outbits |= (1<step_event_count;
- if (st.exec_block->direction_bits & (1<entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed.
- pl_block = NULL; // Flag st_prep_segment() to load new velocity profile.
- }
-}
-
-
-/* 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
- algorithm only executes steps within the segment buffer and is filled by the main program
- when steps are "checked-out" from the first block in the planner buffer. This keeps the
- 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.
- Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
- NOTE: Computation units are in steps, millimeters, and minutes.
-*/
-void st_prep_buffer()
-{
- while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
-
- // Determine if we need to load a new planner block or if the block has been replanned.
- if (pl_block == NULL) {
- pl_block = plan_get_current_block(); // Query planner for a queued block
- if (pl_block == NULL) { return; } // No planner blocks. Exit.
-
- // Check if the segment buffer completed the last planner block. If so, load the Bresenham
- // data for the block. If not, we are still mid-block and the velocity profile was updated.
- if (prep.flag_partial_block) {
- prep.flag_partial_block = false; // Reset flag
- } else {
- // Increment stepper common data index to store new planner block data.
- if ( ++prep.st_block_index == (SEGMENT_BUFFER_SIZE-1) ) { prep.st_block_index = 0; }
-
- // 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.
- st_prep_block = &st_block_buffer[prep.st_block_index];
- st_prep_block->direction_bits = pl_block->direction_bits;
- #ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
- st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
- st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
- st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
- st_prep_block->steps[C_AXIS] = pl_block->steps[C_AXIS];
- 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
- // 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.
- st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS] << MAX_AMASS_LEVEL;
- st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS] << MAX_AMASS_LEVEL;
- st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS] << MAX_AMASS_LEVEL;
- st_prep_block->steps[C_AXIS] = pl_block->steps[C_AXIS] << 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 = 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 planner block
-
- if (sys.state == STATE_HOLD) {
- // Override planner block entry speed and enforce deceleration during feed hold.
- 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); }
- }
-
- /* ---------------------------------------------------------------------------------
- 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
- 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.
- float inv_2_accel = 0.5/pl_block->acceleration;
- if (sys.state == STATE_HOLD) { // [Forced Deceleration to Zero Velocity]
- // Compute velocity profile parameters for a feed hold in-progress. This profile overrides
- // the planner block profile, enforcing a deceleration to zero speed.
- prep.ramp_type = RAMP_DECEL;
- // Compute decelerate distance relative to end of block.
- float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr;
- if (decel_dist < 0.0) {
- // Deceleration through entire planner block. End of feed hold is not in this block.
- prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
- } else {
- prep.mm_complete = decel_dist; // End of feed hold.
- prep.exit_speed = 0.0;
- }
- } 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.exit_speed = plan_get_exec_block_exit_speed();
- float exit_speed_sqr = prep.exit_speed*prep.exit_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 (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);
- 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) {
- // 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);
- }
- } 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;
- }
- } else { // Acceleration-only type
- prep.accelerate_until = 0.0;
- // prep.decelerate_after = 0.0;
- prep.maximum_speed = prep.exit_speed;
- }
- }
- }
-
- // Initialize new segment
- segment_t *prep_segment = &segment_buffer[segment_buffer_head];
-
- // Set new segment to point to the current segment data block.
- prep_segment->st_block_index = prep.st_block_index;
-
- /*------------------------------------------------------------------------------------
- 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
- 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
- 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,
- 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 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:
- // 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);
- if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
- // Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
- 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);
- 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.
- prep.current_speed += speed_var;
- }
- break;
- 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
- // 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.
- // 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;
- }
- break;
- default: // case RAMP_DECEL:
- // NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed.
- speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min)
- if (prep.current_speed > speed_var) { // Check if at or below zero speed.
- // Compute distance from end of segment to end of block.
- mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm)
- if (mm_var > prep.mm_complete) { // Deceleration only.
- mm_remaining = mm_var;
- prep.current_speed -= speed_var;
- break; // Segment complete. Exit switch-case statement. Continue do-while loop.
- }
- } // 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;
- }
- dt += time_var; // Add computed ramp time to total segment time.
- if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
- else {
- if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps.
- // Increase segment time to ensure at least one step in segment. Override and loop
- // through distance calculations until minimum_mm or mm_complete.
- dt_max += DT_SEGMENT;
- time_var = dt_max - dt;
- } 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
- 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
- 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).
- */
- float steps_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps
- float n_steps_remaining = ceil(steps_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.state == STATE_HOLD) {
-
- // Less than one step to decelerate to zero speed, but already very close. AMASS
- // requires full steps to execute. So, just bail.
- prep.current_speed = 0.0;
- prep.dt_remainder = 0.0;
- prep.steps_remaining = n_steps_remaining;
- pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
- plan_cycle_reinitialize();
- sys.state = STATE_QUEUED;
- 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
- // 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
- // 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 - steps_remaining); // Compute adjusted step rate inverse
- prep.dt_remainder = (n_steps_remaining - steps_remaining)*inv_rate; // Update segment partial step time
-
- // Compute CPU cycles per step for the prepped segment.
- uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
-
- #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;
- 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
- // Compute step timing and timer prescalar for normal step generation.
- if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
- prep_segment->prescaler = 1; // prescaler: 0
- prep_segment->cycles_per_tick = cycles;
- } else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
- prep_segment->prescaler = 2; // prescaler: 8
- prep_segment->cycles_per_tick = cycles >> 3;
- } else {
- prep_segment->prescaler = 3; // prescaler: 64
- if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
- prep_segment->cycles_per_tick = cycles >> 6;
- } else { // Just set the slowest speed possible. (Around 4 step/sec.)
- prep_segment->cycles_per_tick = 0xffff;
- }
- }
- #endif
-
- // Segment complete! Increment segment buffer indices.
- segment_buffer_head = segment_next_head;
- if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
-
- // Setup initial conditions for next segment.
- if (mm_remaining > prep.mm_complete) {
- // Normal operation. Block incomplete. Distance remaining in block to be executed.
- pl_block->millimeters = mm_remaining;
- prep.steps_remaining = steps_remaining;
- } else {
- // 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.
- // NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
- prep.current_speed = 0.0;
- prep.dt_remainder = 0.0;
- prep.steps_remaining = ceil(steps_remaining);
- pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
- plan_cycle_reinitialize();
- sys.state = STATE_QUEUED; // End cycle.
-
- return; // Bail!
-// TODO: Try to move QUEUED setting into cycle re-initialize.
-
- } else { // End of planner block
- // The planner block is complete. All steps are set to be executed in the segment buffer.
- pl_block = NULL;
- plan_discard_current_block();
- }
- }
-
- }
-}
-
-
-// Called by runtime 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)){
- return prep.current_speed;
- }
- return 0.0f;
- }
-#endif
+/*
+ stepper.c - stepper motor driver: executes motion plans using stepper motors
+ Part of Grbl
+
+ 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
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+*/
+
+#include "grbl.h"
+
+
+// Some useful constants.
+#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
+// 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
+// and timer accuracy. Do not alter these settings unless you know what you are doing.
+#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
+ #define MAX_AMASS_LEVEL 3
+ // AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
+ #define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
+ #define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4)
+ #define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)
+
+ #if MAX_AMASS_LEVEL <= 0
+ error "AMASS must have 1 or more levels to operate correctly."
+ #endif
+#endif
+
+
+// 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 {
+ uint32_t steps[N_AXIS];
+ uint32_t step_event_count;
+ uint8_t direction_bits;
+ #ifdef VARIABLE_SPINDLE
+ uint8_t is_pwm_rate_adjusted; // Tracks motions that require constant laser power/rate
+ #endif
+} 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
+// 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
+// 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
+ uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
+ uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
+ #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
+ uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
+ #else
+ uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
+ #endif
+ #ifdef VARIABLE_SPINDLE
+ uint8_t spindle_pwm;
+ #endif
+} segment_t;
+static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
+
+// Stepper ISR data struct. Contains the running data for the main stepper ISR.
+typedef struct {
+ // Used by the bresenham line algorithm
+ uint32_t counter_x, // Counter variables for the bresenham line tracer
+ counter_y,
+ counter_z,
+ counter_c; // C Axis
+#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
+ uint8_t dir_outbits;
+ #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
+ uint32_t steps[N_AXIS];
+ #endif
+
+ 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
+} stepper_t;
+static stepper_t st;
+
+// Step segment ring buffer indices
+static volatile uint8_t segment_buffer_tail;
+static uint8_t segment_buffer_head;
+static uint8_t segment_next_head;
+
+// Step and direction port invert masks.
+static uint8_t step_port_invert_mask;
+static uint8_t dir_port_invert_mask;
+
+// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
+static volatile uint8_t busy;
+
+// 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
+
+// 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;
+ float req_mm_increment;
+
+ #ifdef PARKING_ENABLE
+ uint8_t last_st_block_index;
+ float last_steps_remaining;
+ float last_step_per_mm;
+ float last_dt_remainder;
+ #endif
+
+ uint8_t ramp_type; // Current segment ramp state
+ float mm_complete; // End of velocity profile from end of current planner block in (mm).
+ // NOTE: This value must coincide with a step(no mantissa) when converted.
+ float current_speed; // Current speed at the end of the segment buffer (mm/min)
+ float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min)
+ float exit_speed; // Exit speed of executing block (mm/min)
+ float accelerate_until; // Acceleration ramp end measured from end of block (mm)
+ float decelerate_after; // Deceleration ramp start measured from end of block (mm)
+
+ #ifdef VARIABLE_SPINDLE
+ float inv_rate; // Used by PWM laser mode to speed up segment calculations.
+ uint8_t current_spindle_pwm;
+ #endif
+} st_prep_t;
+static st_prep_t prep;
+
+
+/* BLOCK VELOCITY PROFILE DEFINITION
+ __________________________
+ /| |\ _________________ ^
+ / | | \ /| |\ |
+ / | | \ / | | \ s
+ / | | | | | \ p
+ / | | | | | \ e
+ +-----+------------------------+---+--+---------------+----+ e
+ | 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
+ stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise-
+ deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
+ triangle(no cruise).
+
+ maximum_speed (< nominal_speed) -> +
+ +--------+ <- maximum_speed (= nominal_speed) /|\
+ / \ / | \
+ current_speed -> + \ / | + <- exit_speed
+ | + <- 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
+ 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()
+{
+ // Enable stepper drivers.
+ if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<> 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<prescaler<cycles_per_tick;
+ st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
+ // If the new segment starts a new planner block, initialize stepper variables and counters.
+ // NOTE: When the segment data index changes, this indicates a new planner block.
+ 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.counter_c = (st.exec_block->step_event_count >> 1);
+ }
+ 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.
+ st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
+ 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;
+ st.steps[C_AXIS] = st.exec_block->steps[C_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_segment->spindle_pwm);
+ #endif
+
+ } else {
+ // Segment buffer empty. Shutdown.
+ st_go_idle();
+ #ifdef VARIABLE_SPINDLE
+ // Ensure pwm is set properly upon completion of rate-controlled motion.
+ if (st.exec_block->is_pwm_rate_adjusted) { spindle_set_speed(SPINDLE_PWM_OFF_VALUE); }
+ #endif
+ system_set_exec_state_flag(EXEC_CYCLE_STOP); // Flag main program for cycle end
+ return; // Nothing to do but exit.
+ }
+ }
+
+
+ // Check probing state.
+ if (sys_probe_state == PROBE_ACTIVE) { probe_state_monitor(); }
+
+ // Reset step out bits.
+ 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
+ if (st.counter_x > st.exec_block->step_event_count) {
+ st.step_outbits |= (1<step_event_count;
+ if (st.exec_block->direction_bits & (1<steps[Y_AXIS];
+ #endif
+ if (st.counter_y > st.exec_block->step_event_count) {
+ st.step_outbits |= (1<step_event_count;
+ if (st.exec_block->direction_bits & (1<steps[Z_AXIS];
+ #endif
+ if (st.counter_z > st.exec_block->step_event_count) {
+ st.step_outbits |= (1<step_event_count;
+ if (st.exec_block->direction_bits & (1<steps[C_AXIS];
+ #endif
+ if (st.counter_c > st.exec_block->step_event_count) {
+ st.step_outbits |= (1<step_event_count;
+ if (st.exec_block->direction_bits & (1<entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed.
+ pl_block = NULL; // Flag st_prep_segment() to load and check active velocity profile.
+ }
+}
+
+
+// Increments the step segment buffer block data ring buffer.
+static uint8_t st_next_block_index(uint8_t block_index)
+{
+ block_index++;
+ if ( block_index == (SEGMENT_BUFFER_SIZE-1) ) { return(0); }
+ return(block_index);
+}
+
+
+#ifdef PARKING_ENABLE
+ // Changes the run state of the step segment buffer to execute the special parking motion.
+ void st_parking_setup_buffer()
+ {
+ // Store step execution data of partially completed block, if necessary.
+ if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
+ prep.last_st_block_index = prep.st_block_index;
+ prep.last_steps_remaining = prep.steps_remaining;
+ prep.last_dt_remainder = prep.dt_remainder;
+ prep.last_step_per_mm = prep.step_per_mm;
+ }
+ // Set flags to execute a parking motion
+ prep.recalculate_flag |= PREP_FLAG_PARKING;
+ prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE);
+ pl_block = NULL; // Always reset parking motion to reload new block.
+ }
+
+
+ // Restores the step segment buffer to the normal run state after a parking motion.
+ void st_parking_restore_buffer()
+ {
+ // 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;
+ prep.steps_remaining = prep.last_steps_remaining;
+ prep.dt_remainder = prep.last_dt_remainder;
+ prep.step_per_mm = prep.last_step_per_mm;
+ prep.recalculate_flag = (PREP_FLAG_HOLD_PARTIAL_BLOCK | PREP_FLAG_RECALCULATE);
+ prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm; // Recompute this value.
+ } else {
+ prep.recalculate_flag = false;
+ }
+ pl_block = NULL; // Set to reload next block.
+ }
+#endif
+
+
+/* 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
+ algorithm only executes steps within the segment buffer and is filled by the main program
+ when steps are "checked-out" from the first block in the planner buffer. This keeps the
+ 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.
+ Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
+ NOTE: Computation units are in steps, millimeters, and minutes.
+*/
+void st_prep_buffer()
+{
+ // Block step prep buffer, while in a suspend state and there is no suspend motion to execute.
+ if (bit_istrue(sys.step_control,STEP_CONTROL_END_MOTION)) { return; }
+
+ while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
+
+ // Determine if we need to load a new planner block or if the block needs to be recomputed.
+ if (pl_block == NULL) {
+
+ // 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) {
+
+ #ifdef PARKING_ENABLE
+ if (prep.recalculate_flag & PREP_FLAG_PARKING) { prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE); }
+ else { prep.recalculate_flag = false; }
+ #else
+ prep.recalculate_flag = false;
+ #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.
+ st_prep_block = &st_block_buffer[prep.st_block_index];
+ st_prep_block->direction_bits = pl_block->direction_bits;
+
+ uint8_t idx;
+ #ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
+ for (idx=0; idxsteps[idx] = (pl_block->steps[idx] << 1); }
+ st_prep_block->step_event_count = (pl_block->step_event_count << 1);
+ #else
+ // 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; idxsteps[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) || (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;
+ pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
+ prep.recalculate_flag &= ~(PREP_FLAG_DECEL_OVERRIDE);
+ } else {
+ prep.current_speed = sqrt(pl_block->entry_speed_sqr);
+ }
+
+ #ifdef VARIABLE_SPINDLE
+ // Setup laser mode variables. PWM rate adjusted motions will always complete a motion with the
+ // spindle off.
+ st_prep_block->is_pwm_rate_adjusted = false;
+ if (settings.flags & BITFLAG_LASER_MODE) {
+ if (pl_block->condition & PL_COND_FLAG_SPINDLE_CCW) {
+ // Pre-compute inverse programmed rate to speed up PWM updating per step segment.
+ prep.inv_rate = 1.0/pl_block->programmed_rate;
+ st_prep_block->is_pwm_rate_adjusted = true;
+ }
+ }
+ #endif
+ }
+
+ /* ---------------------------------------------------------------------------------
+ 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
+ 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.
+ float inv_2_accel = 0.5/pl_block->acceleration;
+ if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { // [Forced Deceleration to Zero Velocity]
+ // Compute velocity profile parameters for a feed hold in-progress. This profile overrides
+ // the planner block profile, enforcing a deceleration to zero speed.
+ prep.ramp_type = RAMP_DECEL;
+ // Compute decelerate distance relative to end of block.
+ float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr;
+ if (decel_dist < 0.0) {
+ // Deceleration through entire planner block. End of feed hold is not in this block.
+ prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
+ } else {
+ prep.mm_complete = decel_dist; // End of feed hold.
+ prep.exit_speed = 0.0;
+ }
+ } 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;
+
+ 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);
+ }
+
+ 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 (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); // Should always be >= 0.0 due to planner reinit.
+ 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*(nominal_speed_sqr-exit_speed_sqr);
+ if (prep.decelerate_after < intersect_distance) { // Trapezoid type
+ 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*(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;
+ }
+ } else { // Acceleration-only type
+ prep.accelerate_until = 0.0;
+ // prep.decelerate_after = 0.0;
+ prep.maximum_speed = prep.exit_speed;
+ }
+ }
+
+ #ifdef VARIABLE_SPINDLE
+ bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); // Force update whenever updating block.
+ #endif
+ }
+
+ // Initialize new segment
+ segment_t *prep_segment = &segment_buffer[segment_buffer_head];
+
+ // Set new segment to point to the current segment data block.
+ prep_segment->st_block_index = prep.st_block_index;
+
+ /*------------------------------------------------------------------------------------
+ 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
+ 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
+ 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,
+ 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 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_DECEL_OVERRIDE:
+ speed_var = pl_block->acceleration*time_var;
+ if (prep.current_speed-prep.maximum_speed <= speed_var) {
+ // Cruise or cruise-deceleration types only for deceleration override.
+ mm_remaining = prep.accelerate_until;
+ 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.
+ mm_remaining -= time_var*(prep.current_speed - 0.5*speed_var);
+ 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);
+ if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
+ // Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
+ 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);
+ 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.
+ prep.current_speed += speed_var;
+ }
+ break;
+ 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
+ // 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.
+ // 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;
+ }
+ break;
+ default: // case RAMP_DECEL:
+ // NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed.
+ speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min)
+ if (prep.current_speed > speed_var) { // Check if at or below zero speed.
+ // Compute distance from end of segment to end of block.
+ mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm)
+ if (mm_var > prep.mm_complete) { // Typical case. In deceleration ramp.
+ mm_remaining = mm_var;
+ prep.current_speed -= speed_var;
+ break; // Segment complete. Exit switch-case statement. Continue do-while loop.
+ }
+ }
+ // 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;
+ prep.current_speed = prep.exit_speed;
+ }
+ dt += time_var; // Add computed ramp time to total segment time.
+ if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
+ else {
+ if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps.
+ // Increase segment time to ensure at least one step in segment. Override and loop
+ // through distance calculations until minimum_mm or mm_complete.
+ dt_max += DT_SEGMENT;
+ time_var = dt_max - dt;
+ } else {
+ break; // **Complete** Exit loop. Segment execution time maxed.
+ }
+ }
+ } while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
+
+ #ifdef VARIABLE_SPINDLE
+ /* -----------------------------------------------------------------------------------
+ Compute spindle speed PWM output for step segment
+ */
+
+ if (st_prep_block->is_pwm_rate_adjusted || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) {
+ if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) {
+ float rpm = pl_block->spindle_speed;
+ // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate.
+ if (st_prep_block->is_pwm_rate_adjusted) { rpm *= (prep.current_speed * prep.inv_rate); }
+ // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE)
+ // but this would be instantaneous only and during a motion. May not matter at all.
+ prep.current_spindle_pwm = spindle_compute_pwm_value(rpm);
+ } else {
+ sys.spindle_speed = 0.0;
+ prep.current_spindle_pwm = SPINDLE_PWM_OFF_VALUE;
+ }
+ bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM);
+ }
+ prep_segment->spindle_pwm = prep.current_spindle_pwm; // Reload segment PWM value
+
+ #endif
+
+ /* -----------------------------------------------------------------------------------
+ Compute segment step rate, steps to execute, and apply necessary rate corrections.
+ 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
+ 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).
+ */
+ float step_dist_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps
+ 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
+ // 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; }
+ #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
+ // 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
+ // 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)
+
+ #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;
+ 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
+ // Compute step timing and timer prescalar for normal step generation.
+ if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
+ prep_segment->prescaler = 1; // prescaler: 0
+ prep_segment->cycles_per_tick = cycles;
+ } else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
+ prep_segment->prescaler = 2; // prescaler: 8
+ prep_segment->cycles_per_tick = cycles >> 3;
+ } else {
+ prep_segment->prescaler = 3; // prescaler: 64
+ if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
+ prep_segment->cycles_per_tick = cycles >> 6;
+ } else { // Just set the slowest speed possible. (Around 4 step/sec.)
+ prep_segment->cycles_per_tick = 0xffff;
+ }
+ }
+ #endif
+
+ // Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it.
+ segment_buffer_head = segment_next_head;
+ if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
+
+ // Update the appropriate planner and segment data.
+ 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) {
+ // 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
+ // cycle stop flag from the ISR. Prep_segment is blocked until then.
+ 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; }
+ #endif
+ return; // Bail!
+ } else { // End of planner block
+ // The planner block is complete. All steps are set to be executed in the segment buffer.
+ 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.
+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;
+}
diff --git a/stepper.h b/stepper.h
index 0e8be87d7..41871a66e 100644
--- a/stepper.h
+++ b/stepper.h
@@ -1,8 +1,9 @@
/*
stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2012-2014 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
@@ -17,15 +18,9 @@
You should have received a copy of the GNU General Public License
along with Grbl. If not, see .
*/
-/*
- This file is based on work from Grbl v0.8, distributed under the
- terms of the MIT-license. See COPYING for more details.
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011 Sungeun K. Jeon
-*/
#ifndef stepper_h
-#define stepper_h
+#define stepper_h
#ifndef SEGMENT_BUFFER_SIZE
#define SEGMENT_BUFFER_SIZE 6
@@ -34,7 +29,7 @@
// Initialize and setup the stepper motor subsystem
void stepper_init();
-// Enable steppers, but cycle does not start unless called by motion control or runtime command.
+// Enable steppers, but cycle does not start unless called by motion control or realtime command.
void st_wake_up();
// Immediately disables steppers
@@ -43,18 +38,22 @@ 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();
-
-// Reloads step segment buffer. Called continuously by runtime execution system.
+
+// Changes the run state of the step segment buffer to execute the special parking motion.
+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();
// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters();
-// Called by runtime status reporting if realtime rate reporting is enabled in config.h.
-#ifdef REPORT_REALTIME_RATE
+// Called by realtime status reporting if realtime rate reporting is enabled in config.h.
float st_get_realtime_rate();
-#endif
#endif
diff --git a/system.c b/system.c
index 9079820cf..b6e972b41 100644
--- a/system.c
+++ b/system.c
@@ -1,199 +1,435 @@
-/*
- system.c - Handles system level commands and real-time processes
- Part of Grbl v0.9
-
- Copyright (c) 2014 Sungeun K. Jeon
-
- Grbl is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Grbl is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Grbl. If not, see .
-*/
-
-#include "system.h"
-#include "settings.h"
-#include "gcode.h"
-#include "motion_control.h"
-#include "report.h"
-#include "print.h"
-
-
-void system_init()
-{
- PINOUT_DDR &= ~(PINOUT_MASK); // Configure as input pins
- PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation.
- PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt
- PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt
-}
-
-
-// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
-// only the runtime command execute variable to have the main program execute these when
-// its ready. This works exactly like the character-based runtime commands when picked off
-// directly from the incoming serial data stream.
-ISR(PINOUT_INT_vect)
-{
- // Enter only if any pinout pin is actively low.
- if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) {
- if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) {
- mc_reset();
- } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) {
- bit_true(sys.execute, EXEC_FEED_HOLD);
- } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) {
- bit_true(sys.execute, EXEC_CYCLE_START);
- }
- }
-}
-
-
-// Executes user startup script, if stored.
-void system_execute_startup(char *line)
-{
- uint8_t n;
- for (n=0; n < N_STARTUP_LINE; n++) {
- if (!(settings_read_startup_line(n, line))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- } else {
- if (line[0] != 0) {
- printString(line); // Echo startup line to indicate execution.
- report_status_message(gc_execute_line(line));
- }
- }
- }
-}
-
-
-// Directs and executes one line of formatted input from protocol_process. While mostly
-// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
-// settings, initiating the homing cycle, and toggling switch states. This differs from
-// the runtime command module by being susceptible to when Grbl is ready to execute the
-// next line during a cycle, so for switches like block delete, the switch only effects
-// the lines that are processed afterward, not necessarily real-time during a cycle,
-// since there are motions already stored in the buffer. However, this 'lag' should not
-// be an issue, since these commands are not typically used during a cycle.
-uint8_t 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 '$' : // Prints Grbl settings
- if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
- if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print.
- else { report_grbl_settings(); }
- break;
- case 'G' : // Prints gcode parser state
- if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
- else { report_gcode_modes(); }
- break;
- case 'C' : // Set check g-code mode [IDLE/CHECK]
- if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
- // Perform reset when toggling off. Check g-code mode should only work if Grbl
- // is idle and ready, regardless of alarm locks. This is mainly to keep things
- // simple and consistent.
- if ( sys.state == STATE_CHECK_MODE ) {
- mc_reset();
- report_feedback_message(MESSAGE_DISABLED);
- } else {
- if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode.
- sys.state = STATE_CHECK_MODE;
- report_feedback_message(MESSAGE_ENABLED);
- }
- break;
- case 'X' : // Disable alarm lock [ALARM]
- if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
- if (sys.state == STATE_ALARM) {
- report_feedback_message(MESSAGE_ALARM_UNLOCK);
- sys.state = STATE_IDLE;
- // Don't run startup script. Prevents stored moves in startup from causing accidents.
- } // 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 runtime commands except for e-stop. The jogging function is intended to
- // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
- // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
- // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
- // motion by repeatedly toggling to slow the motion to the desired location. Location data would
- // need to be updated real-time and supplied to the user through status queries.
- // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
- // handled by the planner. It would be possible for the jog subprogram to insert blocks into the
- // block buffer without having the planner plan them. It would need to manage de/ac-celerations
- // on its own carefully. This approach could be effective and possibly size/memory efficient.
- 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] ) {
- case '#' : // Print Grbl NGC parameters
- if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
- else { report_ngc_parameters(); }
- break;
- case 'H' : // Perform homing cycle [IDLE/ALARM]
- if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
- // Only perform homing if Grbl is idle or lost.
- mc_homing_cycle();
- if (!sys.abort) { system_execute_startup(line); } // Execute startup scripts after successful homing.
- } else { return(STATUS_SETTING_DISABLED); }
- break;
- case 'I' : // Print or store build info. [IDLE/ALARM]
- 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);
- }
- break;
- case 'N' : // Startup lines. [IDLE/ALARM]
- if ( line[++char_counter] == 0 ) { // Print startup lines
- for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
- if (!(settings_read_startup_line(helper_var, line))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- } else {
- report_startup_line(helper_var,line);
- }
- }
- break;
- } else { // Store startup line [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.
- // No break. Continues into default: to read remaining command characters.
- }
- default : // Storing setting methods [IDLE/ALARM]
- if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
- if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
- if (helper_var) { // Store startup line
- // Prepare sending gcode block to gcode parser by shifting all characters
- helper_var = char_counter; // Set helper variable as counter to start of gcode block
- do {
- line[char_counter-helper_var] = line[char_counter];
- } while (line[char_counter++] != 0);
- // Execute gcode block to ensure block is valid.
- helper_var = gc_execute_line(line); // Set helper_var to returned status code.
- if (helper_var) { return(helper_var); }
- else {
- helper_var = trunc(parameter); // Set helper_var to int value of parameter
- settings_store_startup_line(helper_var,line);
- }
- } else { // Store global setting.
- if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
- if(line[char_counter] != 0) { return(STATUS_INVALID_STATEMENT); }
- return(settings_store_global_setting((uint8_t)parameter, value));
- }
- }
- }
- return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
-}
+/*
+ system.c - Handles system level commands and real-time processes
+ Part of Grbl
+
+ 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+*/
+
+#include "grbl.h"
+
+
+void system_init()
+{
+ CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins
+ #ifdef DISABLE_CONTROL_PIN_PULL_UP
+ CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down.
+ #else
+ CONTROL_PORT |= CONTROL_MASK; // Enable internal pull-up resistors. Normal high operation.
+ #endif
+ CONTROL_PCMSK |= CONTROL_MASK; // Enable specific pins of the Pin Change Interrupt
+ PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt
+}
+
+
+// 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()
+{
+ uint8_t control_state = 0;
+ uint8_t pin = (CONTROL_PIN & CONTROL_MASK) ^ CONTROL_MASK;
+ #ifdef INVERT_CONTROL_PIN_MASK
+ pin ^= INVERT_CONTROL_PIN_MASK;
+ #endif
+ if (pin) {
+ #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
+ if (bit_istrue(pin,(1< 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 = 0;
+}
+
+
+// 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.
+float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
+{
+ float pos;
+ #ifdef COREXY
+ 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];
+ }
+ #else
+ pos = steps[idx]/settings.steps_per_mm[idx];
+ #endif
+ return(pos);
+}
+
+
+void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
+{
+ uint8_t idx;
+ for (idx=0; 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();
+ sys_rt_exec_state |= (mask);
+ SREG = sreg;
+}
+
+void system_clear_exec_state_flag(uint8_t mask) {
+ uint8_t sreg = SREG;
+ cli();
+ sys_rt_exec_state &= ~(mask);
+ SREG = sreg;
+}
+
+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() {
+ uint8_t sreg = SREG;
+ cli();
+ sys_rt_exec_alarm = 0;
+ 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;
+}
diff --git a/system.h b/system.h
index 93ab2b96a..cfc927384 100644
--- a/system.h
+++ b/system.h
@@ -1,8 +1,8 @@
/*
system.h - Header for system level commands and real-time processes
- Part of Grbl v0.9
+ Part of Grbl
- Copyright (c) 2014 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
@@ -21,78 +21,192 @@
#ifndef system_h
#define system_h
-// Define system header files and standard libraries used by Grbl
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Define Grbl configuration and shared header files
-#include "config.h"
-#include "defaults.h"
-#include "cpu_map.h"
-#include "nuts_bolts.h"
-
-
-// Define system executor bit map. Used internally by runtime protocol as runtime command flags,
-// which notifies the main program to execute the specified runtime command asynchronously.
+#include "grbl.h"
+
+// 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 runtime protocol only needs to check for a non-zero value to
-// know when there is a runtime command to execute.
+// 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
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000
#define EXEC_RESET bit(4) // bitmask 00010000
-#define EXEC_ALARM bit(5) // bitmask 00100000
-#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000
-// #define bit(7) // bitmask 10000000
+#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
+#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
+#define EXEC_SLEEP bit(7) // bitmask 10000000
+
+// 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
+#define EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH 10
+
+// 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
// critical events.
-#define STATE_IDLE 0 // Must be zero. No flags.
-#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access.
-#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only.
-#define STATE_HOMING bit(2) // Performing homing cycle
-#define STATE_QUEUED bit(3) // Indicates buffered blocks, awaiting cycle start.
-#define STATE_CYCLE bit(4) // Cycle is running
-#define STATE_HOLD bit(5) // Executing feed hold
-// #define STATE_JOG bit(6) // Jogging mode is unique like homing.
+#define STATE_IDLE 0 // Must be zero. No flags.
+#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access.
+#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only.
+#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_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.
+
+// Define system suspend flags. Used in various ways to manage suspend states and procedures.
+#define SUSPEND_DISABLE 0 // Must be zero.
+#define SUSPEND_HOLD_COMPLETE bit(0) // Indicates initial feed hold is complete.
+#define SUSPEND_RESTART_RETRACT bit(1) // Flag to indicate a retract from a restore parking motion.
+#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) // 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 // Must be zero.
+#define STEP_CONTROL_END_MOTION bit(0)
+#define STEP_CONTROL_EXECUTE_HOLD bit(1)
+#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2)
+#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3)
+
+// Define control pin index for Grbl internal use. Pin maps may change, but these values don't.
+#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
+ #define N_CONTROL_PIN 4
+ #define CONTROL_PIN_INDEX_SAFETY_DOOR bit(0)
+ #define CONTROL_PIN_INDEX_RESET bit(1)
+ #define CONTROL_PIN_INDEX_FEED_HOLD bit(2)
+ #define CONTROL_PIN_INDEX_CYCLE_START bit(3)
+#else
+ #define N_CONTROL_PIN 3
+ #define CONTROL_PIN_INDEX_RESET bit(0)
+ #define CONTROL_PIN_INDEX_FEED_HOLD bit(1)
+ #define CONTROL_PIN_INDEX_CYCLE_START bit(2)
+#endif
+
+// Define spindle stop override control states.
+#define SPINDLE_STOP_OVR_DISABLED 0 // Must be zero.
+#define SPINDLE_STOP_OVR_ENABLED bit(0)
+#define SPINDLE_STOP_OVR_INITIATE bit(1)
+#define SPINDLE_STOP_OVR_RESTORE bit(2)
+#define SPINDLE_STOP_OVR_RESTORE_CYCLE bit(3)
// Define global system variables
typedef struct {
- uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
- uint8_t state; // Tracks the current state of Grbl.
- volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks.
- uint8_t homing_axis_lock;
- int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
- // NOTE: This may need to be a volatile variable, if problems arise.
- uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
- volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
- int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
+ uint8_t state; // Tracks the current system state of Grbl.
+ uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
+ 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.
+ #ifdef ENABLE_DUAL_AXIS
+ uint8_t homing_axis_lock_dual;
+ #endif
+ 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 spindle_stop_ovr; // Tracks spindle stop 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.
+ #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
+ uint8_t override_ctrl; // Tracks override control states.
+ #endif
+ #ifdef VARIABLE_SPINDLE
+ float spindle_speed;
+ #endif
} system_t;
extern system_t sys;
+// NOTE: These position variables may need to be declared as volatiles, if problems arise.
+extern int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
+extern int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
+
+extern volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
+extern volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
+extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
+extern volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
+extern 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)
+ extern volatile uint8_t sys_rt_exec_debug;
+#endif
// Initialize the serial protocol
void system_init();
+// Returns bitfield of control pin states, organized by CONTROL_PIN_INDEX. (1=triggered, 0=not triggered).
+uint8_t system_control_get_state();
+
+// Returns if safety door is open or closed, based on pin state.
+uint8_t system_check_safety_door_ajar();
+
// Executes an internal system command, defined as a string starting with a '$'
uint8_t system_execute_line(char *line);
-// Checks and executes a runtime command at various stop points in main program
-void system_execute_runtime();
-
// 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(uint8_t code);
+void system_clear_exec_alarm();
+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