Merge branch 'dev' into edge

Conflicts:
	sim/simulator.c
	sim/simulator.h
This commit is contained in:
Sonny Jeon 2014-07-09 09:52:57 -06:00
commit 23ed7b6d4b
71 changed files with 14097 additions and 2866 deletions

View File

@ -33,7 +33,7 @@ CLOCK = 16000000
PROGRAMMER ?= -c avrisp2 -P usb
OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \
protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \
print.o report.o
print.o probe.o report.o system.o
# FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m
# update that line with this when programmer is back up:
@ -84,7 +84,7 @@ main.elf: $(OBJECTS)
grbl.hex: main.elf
rm -f grbl.hex
avr-objcopy -j .text -j .data -O ihex main.elf grbl.hex
avr-size -C --mcu=$(DEVICE) main.elf
avr-size --format=berkeley 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.
@ -97,4 +97,3 @@ cpp:
# include generated header dependencies
-include $(OBJECTS:.o=.d)

View File

@ -1,25 +1,34 @@
#Grbl - An embedded g-code interpreter and motion-controller for the Arduino/AVR328 microcontroller
#Grbl
------------
Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328.
This branch serves only as a developmental platform for working on new ideas that may eventually be installed into Grbl's edge branch. Please do not use as there is no guarantee this code base is up-to-date or working.
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 m aintain more than 30kHz of stable, jitter free control pulses.
------------
It accepts standards-compliant G-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, other basic functional g-code commands. Functions and variables are not currently supported, but may be included in future releases in a form of a pre-processor.
Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328. (Other AVR CPUs are unofficially supported as well.)
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, other basic functional g-code commands. Although canned cycles, functions, and variables are not currently supported (may in the future), GUIs can be built or modified easily to translate to straight g-code for Grbl.
Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering.
##Changelog for v0.9 from v0.8
- **ALPHA status: Under heavy development.**
- New stepper algorithm: Based on the Pramod Ranade inverse time algorithm, but modified to ensure steps are executed exactly. This algorithm performs a constant timer tick and has a hard limit of 30kHz maximum step frequency. It is also highly tuneable and should be very easy to port to other microcontroller architectures. Overall, a much better, smoother stepper algorithm with the capability of very high speeds.
- Planner optimizations: Multiple changes to increase planner execution speed and removed redundant variables.
- New 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. Users should audibly hear significant differences in how their machines move and see improved overall performance!
- Planner optimizations: Planning computations improved four-fold or more by optimizing end-to-end operations. Changes include streaming optimizations by ignoring already optimized blocks and removing redundant variables and computations and offloading them to the stepper algorithm to compute on an ad-hoc basis.
- Planner stability: Previous Grbl planners have all had a corruption issue in rare circumstances that becomes particularly problematic at high step frequencies and when jogging. The new planner is robust and incorruptible, meaning that we can fearlessly drive Grbl to it's highest limits. Combined with the new stepper algorithm and planner optimizations, this means 5x to 10x performance increases in our testing! This is all achieved through the introduction of an intermediary step segment buffer that "checks-out" steps from the planner buffer in real-time.
- Acceleration independence: Each axes may be defined with different acceleration parameters and Grbl will automagically calculate the maximum acceleration through a path depending on the direction traveled. This is very useful for machine that have very different axes properties, like the ShapeOko z-axis.
- Maximum velocity independence: As with acceleration, the maximum velocity of individual axes may be defined. All seek/rapids motions will move at these maximum rates, but never exceed any one axes. So, when two or more axes move, the limiting axis will move at its maximum rate, while the other axes are scaled down.
- Significantly improved arc performance: Arcs are now defined in terms of chordal tolerance, rather than segment length. Chordal tolerance will automatically scale all arc line segments depending on arc radius, such that the error does not exceed the tolerance value (default: 0.005 mm.) So, for larger radii arcs, Grbl can move faster through them, because the segments are always longer and the planner has more distance to plan with.
- Significantly improved arc performance: Arcs are now defined in terms of chordal tolerance, rather than segment length. Chordal tolerance will automatically scale all arc line segments depending on arc radius, such that the error does not exceed the tolerance value (default: 0.002 mm.) So, for larger radii arcs, Grbl can move faster through them, because the segments are always longer and the planner has more distance to plan with.
- Soft limits: Checks if any motion command exceeds workspace limits. Alarms out when found. Another safety feature, but, unlike hard limits, position does not get lost, as it forces a feed hold before erroring out.
- 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: 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.
- Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Also reduces soft limits CPU overhead.
- Feedrate overrides: In the works, but planner has begun to be re-factored for this feature.
- Jogging controls: Methodology needs to be to figured out first. Could be dropped due to flash space concerns. Last item on the agenda.
- Homing routine updated: Sets workspace volume in all negative space regardless of limit switch position. Common on pro CNCs. Now tied directly into the main planner and stepper modules to reduce flash space and allow maximum speeds during seeking.
- Combined limit pins capability: 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.
- 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.
- Increased serial baud rate: Default serial baudrate is now 115200, because the new planner and stepper allows much higher processing and execution speeds that 9600 baud was just not cutting it.
- Feedrate overrides: (Slated for v1.0 release) The framework to enable feedrate overrides is in-place with v0.9, but the minor details has not yet been installed.
- Jogging controls: (Slated for v1.0 release) Methodology needs to be to figured out first. Could be dropped due to flash space concerns.
_The project was initially inspired by the Arduino GCode Interpreter by Mike Ellery_

311
config.h
View File

@ -2,8 +2,8 @@
config.h - compile time configuration
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -19,80 +19,24 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef config_h
#define config_h
// 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.
// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them.
#ifndef config_h
#define config_h
// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
#define DEFAULTS_GENERIC
// Serial baud rate
#define BAUD_RATE 9600
#define BAUD_RATE 115200
// Define pin-assignments
// NOTE: All step bit and direction pins must be on the same port.
#define STEPPING_DDR DDRD
#define STEPPING_PORT PORTD
#define X_STEP_BIT 2 // Uno Digital Pin 2
#define Y_STEP_BIT 3 // Uno Digital Pin 3
#define Z_STEP_BIT 4 // Uno Digital Pin 4
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// NOTE: All limit bit pins must be on the same port
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define LIMIT_PORT PORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#define COOLANT_FLOOD_DDR DDRC
#define COOLANT_FLOOD_PORT PORTC
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable.
#ifdef ENABLE_M7
#define COOLANT_MIST_DDR DDRC
#define COOLANT_MIST_PORT PORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
#endif
// NOTE: All pinouts pins must be on the same port
#define PINOUT_DDR DDRC
#define PINOUT_PIN PINC
#define PINOUT_PORT PORTC
#define PIN_RESET 0 // Uno Analog Pin 0
#define PIN_FEED_HOLD 1 // Uno Analog Pin 1
#define PIN_CYCLE_START 2 // Uno Analog Pin 2
#define PINOUT_INT PCIE1 // Pin change interrupt enable pin
#define PINOUT_INT_vect PCINT1_vect
#define PINOUT_PCMSK PCMSK1 // Pin change interrupt register
#define PINOUT_MASK ((1<<PIN_RESET)|(1<<PIN_FEED_HOLD)|(1<<PIN_CYCLE_START))
// 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_ATMEGA328P // Arduino Uno CPU
// Define runtime command special characters. These characters are 'picked-off' directly from the
// serial read data stream and are not passed to the grbl line execution parser. Select characters
@ -103,53 +47,121 @@
#define CMD_STATUS_REPORT '?'
#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
#define CMD_RESET 0x18 // ctrl-x
#define CMD_RESET 0x18 // ctrl-x.
// The "Stepper Driver Interrupt" employs the Pramod Ranade inverse time algorithm to manage the
// Bresenham line stepping algorithm. The value ISR_TICKS_PER_SECOND is the frequency(Hz) at which
// the Ranade algorithm ticks at. Recommended step frequencies are limited by the Ranade frequency by
// approximately 0.75-0.9 * ISR_TICK_PER_SECOND. Meaning for 30kHz, the max step frequency is roughly
// 22.5-27kHz, but 30kHz is still possible, just not optimal. An Arduino can safely complete a single
// interrupt of the current stepper driver algorithm theoretically up to a frequency of 35-40kHz, but
// CPU overhead increases exponentially as this frequency goes up. So there will be little left for
// other processes like arcs.
#define ISR_TICKS_PER_SECOND 30000L // Integer (Hz)
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
// mainly a safety feature to remind the user to home, since position is unknown to Grbl.
#define HOMING_INIT_LOCK // Comment to disable
// The temporal resolution of the acceleration management subsystem. Higher number give smoother
// acceleration but may impact performance. If you run at very high feedrates (>15kHz or so) and
// very high accelerations, this will reduce the error between how the planner plans the velocity
// profiles and how the stepper program actually performs them. The correct value for this parameter
// is machine dependent, so it's advised to set this only as high as needed. Approximate successful
// values can widely range from 50 to 200 or more. Cannot be greater than ISR_TICKS_PER_SECOND/2.
#define ACCELERATION_TICKS_PER_SECOND 120L
// 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
// 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.
// 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
// will not be affected by pin sharing.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // REQUIRED: First move Z to clear workspace.
#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // OPTIONAL: Then move X,Y at the same time.
// #define HOMING_CYCLE_2 // OPTIONAL: Uncomment and add axes mask to enable
// NOTE: Make sure this value is less than 256, when adjusting both dependent parameters.
#define ISR_TICKS_PER_ACCELERATION_TICK (ISR_TICKS_PER_SECOND/ACCELERATION_TICKS_PER_SECOND)
// Number of homing cycles performed after when the machine initially jogs to limit switches.
// This help in preventing overshoot and should improve repeatability. This value should be one or
// greater.
#define N_HOMING_LOCATE_CYCLE 2 // Integer (1-128)
// The Ranade algorithm can use either floating point or long integers for its counters, but for
// integers the counter values must be scaled since these values can be very small (10^-6). This
// multiplier value scales the floating point counter values for use in a long integer. Long integers
// are finite so select the multiplier value high enough to avoid any numerical round-off issues and
// still have enough range to account for all motion types. However, in most all imaginable CNC
// applications, the following multiplier value will work more than well enough. If you do have
// happened to weird stepper motion issues, try modifying this value by adding or subtracting a
// zero and report it to the Grbl administrators.
#define RANADE_MULTIPLIER 100000000.0
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
// and addresses are defined in settings.h. With the current settings, up to 3 startup blocks may
// be stored and executed in order. These startup blocks would typically be used to set the g-code
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-3)
// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end
// of the buffer and all stops. This should not be much greater than zero and should only be changed
// if unwanted behavior is observed on a user's machine when running at very slow speeds.
#define MINIMUM_PLANNER_SPEED 0.0 // (mm/min)
// Number of floating decimal points printed by Grbl for certain value types. These settings are
// determined by realistic and commonly observed values in CNC machines. For example, position
// values cannot be less than 0.001mm or 0.0001in, because machines can not be physically more
// precise this. So, there is likely no need to change these, but you can if you need to here.
// NOTE: Must be an integer value from 0 to ~4. More than 4 may exhibit round-off errors.
#define N_DECIMAL_COORDVALUE_INCH 4 // Coordinate or position value in inches
#define N_DECIMAL_COORDVALUE_MM 3 // Coordinate or position value in mm
#define N_DECIMAL_RATEVALUE_INCH 1 // Rate or velocity value in in/min
#define N_DECIMAL_RATEVALUE_MM 0 // Rate or velocity value in mm/min
#define N_DECIMAL_SETTINGVALUE 3 // Decimals for floating point setting values
// Minimum stepper rate for the "Stepper Driver Interrupt". Sets the absolute minimum stepper rate
// in the stepper program and never runs slower than this value. If the RANADE_MULTIPLIER value
// changes, it will affect how this value works. So, if a zero is add/subtracted from the
// RANADE_MULTIPLIER value, do the same to this value if you want to same response.
// NOTE: Compute by (desired_step_rate/60) * RANADE_MULTIPLIER/ISR_TICKS_PER_SECOND. (mm/min)
#define MINIMUM_STEP_RATE 1000L // Integer (mult*mm/isr_tic)
// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
// goes from 18 or 16 to make room for the additional line number data in the plan_block_t struct
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.
// Minimum stepper rate. Only used by homing at this point. May be removed in later releases.
#define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only
// Allows GRBL to report the real-time feed rate. Enabling this means that GRBL will be reporting more
// data with each status update.
// NOTE: This is experimental and doesn't quite work 100%. Maybe fixed or refactored later.
// #define REPORT_REALTIME_RATE // Disabled by default. Uncomment to enable.
// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
// analog pin 5. Only use this option if you require a second coolant control pin.
// NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless.
// #define ENABLE_M7 // Disabled by default. Uncomment to enable.
// ---------------------------------------------------------------------------------------
// ADVANCED CONFIGURATION OPTIONS:
// The temporal resolution of the acceleration management subsystem. A higher number gives smoother
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
// impact performance. The correct value for this parameter is machine dependent, so it's advised to
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
// NOTE: Changing this value also changes the execution time of a segment in the step segment buffer.
// When increasing this value, this stores less overall time in the segment buffer and vice versa. Make
// certain the step segment buffer is increased/decreased to account for these changes.
#define ACCELERATION_TICKS_PER_SECOND 100
// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
// smoothing the stepping of multi-axis motions. This feature smooths motion particularly at low step
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
// step smoothing. See stepper.c for more details on the AMASS system works.
#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// the selected axis with the tool oriented toward the negative direction. In other words, a positive
// tool length offset value is subtracted from the current location.
#define TOOL_LENGTH_OFFSET_AXIS Z_AXIS // Default z-axis. Valid values are X_AXIS, Y_AXIS, or Z_AXIS.
// Enables variable spindle output voltage for different RPM values. On the Arduino Uno, the spindle
// enable pin will output 5V for maximum RPM with 256 intermediate levels and 0V when disabled.
// NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch!
// The hardware PWM output on pin D11 is required for variable spindle output voltages.
// #define VARIABLE_SPINDLE // Default disabled. Uncomment to enable.
// Use by the variable spindle output only. These parameters set the maximum and minimum spindle speed
// "S" g-code values to correspond to the maximum and minimum pin voltages. There are 256 discrete and
// equally divided voltage bins between the maximum and minimum spindle speeds. So for a 5V pin, 1000
// max rpm, and 250 min rpm, the spindle output voltage would be set for the following "S" commands:
// "S1000" @ 5V, "S250" @ 0.02V, and "S625" @ 2.5V (mid-range). The pin outputs 0V when disabled.
#define SPINDLE_MAX_RPM 1000.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM.
#define SPINDLE_MIN_RPM 0.0 // Min spindle RPM. This value is equal to (1/256) duty cycle on the PWM.
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
// every buffer block junction, except for starting from rest and end of the buffer, which are always
// zero. This value controls how fast the machine moves through junctions with no regard for acceleration
// limits or angle between neighboring block line move directions. This is useful for machines that can't
// tolerate the tool dwelling for a split second, i.e. 3d printers or laser cutters. If used, this value
// should not be much greater than zero or to the minimum value necessary for the machine to work.
#define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min)
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction. This parameter maybe decreased if there are issues with the accuracy of the arc
// generations. In general, the default value is more than enough for the intended CNC applications
// of grbl, and should be on the order or greater than the size of the buffer to help with the
// computational efficiency of generating arcs.
// NOTE: Arcs are now generated by a chordal tolerance
#define N_ARC_CORRECTION 20 // Integer (1-255)
// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
@ -158,52 +170,16 @@
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
// mainly a safety feature to remind the user to home, since position is unknown to Grbl.
#define HOMING_INIT_LOCK // Comment to disable
// The homing cycle seek and feed rates will adjust so all axes independently move at the homing
// seek and feed rates regardless of how many axes are in motion simultaneously. If disabled, rates
// are point-to-point rates, as done in normal operation. For example in an XY diagonal motion, the
// diagonal motion moves at the intended rate, but the individual axes move at 70% speed. This option
// just moves them all at 100% speed.
#define HOMING_RATE_ADJUST // Comment to disable
// Define the homing cycle search patterns with bitmasks. The homing cycle first performs a search
// to engage the limit switches. HOMING_SEARCH_CYCLE_x are executed in order starting with suffix 0
// and searches the enabled axes in the bitmask. This allows for users with non-standard cartesian
// machines, such as a lathe (x then z), to configure the homing cycle behavior to their needs.
// Search cycle 0 is required, but cycles 1 and 2 are both optional and may be commented to disable.
// After the search cycle, homing then performs a series of locating about the limit switches to hone
// in on machine zero, followed by a pull-off maneuver. HOMING_LOCATE_CYCLE governs these final moves,
// and this mask must contain all axes in the search.
// NOTE: Later versions may have this installed in settings.
#define HOMING_SEARCH_CYCLE_0 (1<<Z_AXIS) // First move Z to clear workspace.
#define HOMING_SEARCH_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // Then move X,Y at the same time.
// #define HOMING_SEARCH_CYCLE_2 // Uncomment and add axes mask to enable
#define HOMING_LOCATE_CYCLE ((1<<X_AXIS)|(1<<Y_AXIS)|(1<<Z_AXIS)) // Must contain ALL search axes
// Number of homing cycles performed after when the machine initially jogs to limit switches.
// This help in preventing overshoot and should improve repeatability. This value should be one or
// greater.
#define N_HOMING_LOCATE_CYCLE 2 // Integer (1-128)
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
// and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may
// be stored and executed in order. These startup blocks would typically be used to set the g-code
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-5)
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction. This parameter maybe decreased if there are issues with the accuracy of the arc
// generations. In general, the default value is more than enough for the intended CNC applications
// of grbl, and should be on the order or greater than the size of the buffer to help with the
// computational efficiency of generating arcs.
#define N_ARC_CORRECTION 20 // Integer (1-255)
// ---------------------------------------------------------------------------------------
// FOR ADVANCED USERS ONLY:
// Creates a delay between the direction pin setting and corresponding step pulse by creating
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
// sets the direction pins, and does not immediately set the stepper pins, as it would in
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
// values for certain setups have ranged from 5 to 20us.
// #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
@ -212,15 +188,23 @@
// up with planning new incoming motions as they are executed.
// #define BLOCK_BUFFER_SIZE 18 // 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
// 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
// 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: 50 characters is not a problem except for extreme cases, but the line buffer size
// NOTE: 70 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 50 // Uncomment to override default in protocol.h
// #define LINE_BUFFER_SIZE 70 // 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
@ -241,8 +225,25 @@
// case, please report any successes to grbl administrators!
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// electrical interference on the signal cables from external sources. It's recommended to first
// use shielded signal cables with their shielding connected to ground (old USB/computer cables
// 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.
// ---------------------------------------------------------------------------------------
// TODO: Install compile-time option to send numeric status codes rather than strings.
// ---------------------------------------------------------------------------------------
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
// #if (ISR_TICKS_PER_ACCELERATION_TICK > 255)
// #error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow.
// #endif
// ---------------------------------------------------------------------------------------
#endif

View File

@ -2,7 +2,7 @@
coolant_control.c - coolant control methods
Part of Grbl
Copyright (c) 2012 Sungeun K. Jeon
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
@ -18,48 +18,46 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "system.h"
#include "coolant_control.h"
#include "settings.h"
#include "config.h"
#include "planner.h"
#include "protocol.h"
#include "gcode.h"
#include <avr/io.h>
static uint8_t current_coolant_mode;
void coolant_init()
{
current_coolant_mode = COOLANT_DISABLE;
#if ENABLE_M7
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
#endif
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
coolant_stop();
}
void coolant_stop()
{
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
#endif
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
}
void coolant_run(uint8_t mode)
{
if (mode != current_coolant_mode)
{
plan_synchronize(); // Ensure coolant turns on when specified in program.
if (mode == COOLANT_FLOOD_ENABLE) {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
} else if (mode == COOLANT_MIST_ENABLE) {
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#endif
} else {
coolant_stop();
}
current_coolant_mode = mode;
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) {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
} else if (mode == COOLANT_MIST_ENABLE) {
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#endif
} else {
coolant_stop();
}
}

View File

@ -2,7 +2,7 @@
coolant_control.h - spindle control methods
Part of Grbl
Copyright (c) 2012 Sungeun K. Jeon
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
@ -21,11 +21,6 @@
#ifndef coolant_control_h
#define coolant_control_h
#include <avr/io.h>
#define COOLANT_MIST_ENABLE 2
#define COOLANT_FLOOD_ENABLE 1
#define COOLANT_DISABLE 0 // Must be zero.
void coolant_init();
void coolant_stop();

264
cpu_map.h Normal file
View File

@ -0,0 +1,264 @@
/*
cpu_map.h - CPU and pin mapping configuration file
Part of Grbl
Copyright (c) 2013-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 <http://www.gnu.org/licenses/>.
*/
/* 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 // (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<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
// Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR DDRD
#define DIRECTION_PORT PORTD
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (pinout).
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define LIMIT_PORT PORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define Z_LIMIT_BIT 4 // Uno Digital Pin 12
#else
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#endif
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11
#else
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#endif
#define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR DDRC
#define COOLANT_FLOOD_PORT PORTC
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRC
#define COOLANT_MIST_PORT PORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
#endif
// Define user-control pinouts (cycle start, reset, feed hold) input pins.
// NOTE: All pinouts pins must be on the same port and not on a port with other input pins (limits).
#define PINOUT_DDR DDRC
#define PINOUT_PIN PINC
#define PINOUT_PORT PORTC
#define PIN_RESET 0 // Uno Analog Pin 0
#define PIN_FEED_HOLD 1 // Uno Analog Pin 1
#define PIN_CYCLE_START 2 // Uno Analog Pin 2
#define PINOUT_INT PCIE1 // Pin change interrupt enable pin
#define PINOUT_INT_vect PCINT1_vect
#define PINOUT_PCMSK PCMSK1 // Pin change interrupt register
#define PINOUT_MASK ((1<<PIN_RESET)|(1<<PIN_FEED_HOLD)|(1<<PIN_CYCLE_START))
// Define probe switch input pin.
#define PROBE_DDR DDRC
#define PROBE_PIN PINC
#define PROBE_PORT PORTC
#define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT)
#ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables
#define TCCRA_REGISTER TCCR2A
#define TCCRB_REGISTER TCCR2B
#define OCR_REGISTER OCR2A
#define COMB_BIT COM2A1
#define WAVE0_REGISTER WGM20
#define WAVE1_REGISTER WGM21
#define WAVE2_REGISTER WGM22
#define WAVE3_REGISTER WGM23
// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#define SPINDLE_PWM_DDR SPINDLE_ENABLE_DDR
#define SPINDLE_PWM_PORT SPINDLE_ENABLE_PORT
#define SPINDLE_PWM_BIT SPINDLE_ENABLE_BIT // Shared with SPINDLE_ENABLE.
#endif // End of VARIABLE_SPINDLE
#endif
//----------------------------------------------------------------------------------------
#ifdef CPU_MAP_ATMEGA2560 // (Arduino Mega 2560) Working @EliteEng
// Serial port pins
#define SERIAL_RX USART0_RX_vect
#define SERIAL_UDRE USART0_UDRE_vect
// Increase Buffers to make use of extra SRAM
//#define RX_BUFFER_SIZE 256
//#define TX_BUFFER_SIZE 128
//#define BLOCK_BUFFER_SIZE 36
//#define LINE_BUFFER_SIZE 100
// Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEP_DDR DDRA
#define STEP_PORT PORTA
#define STEP_PIN PINA
#define X_STEP_BIT 2 // MEGA2560 Digital Pin 24
#define Y_STEP_BIT 3 // MEGA2560 Digital Pin 25
#define Z_STEP_BIT 4 // MEGA2560 Digital Pin 26
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
// Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR DDRA
#define DIRECTION_PORT PORTA
#define DIRECTION_PIN PINA
#define X_DIRECTION_BIT 5 // MEGA2560 Digital Pin 27
#define Y_DIRECTION_BIT 6 // MEGA2560 Digital Pin 28
#define Z_DIRECTION_BIT 7 // MEGA2560 Digital Pin 29
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 7 // MEGA2560 Digital Pin 13
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// NOTE: All limit bit pins must be on the same port
#define LIMIT_DDR DDRB
#define LIMIT_PORT PORTB
#define LIMIT_PIN PINB
#define X_LIMIT_BIT 4 // MEGA2560 Digital Pin 10
#define Y_LIMIT_BIT 5 // MEGA2560 Digital Pin 11
#define Z_LIMIT_BIT 6 // MEGA2560 Digital Pin 12
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRH
#define SPINDLE_ENABLE_PORT PORTH
#define SPINDLE_ENABLE_BIT 3 // MEGA2560 Digital Pin 6
#define SPINDLE_DIRECTION_DDR DDRE
#define SPINDLE_DIRECTION_PORT PORTE
#define SPINDLE_DIRECTION_BIT 3 // MEGA2560 Digital Pin 5
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR DDRH
#define COOLANT_FLOOD_PORT PORTH
#define COOLANT_FLOOD_BIT 5 // MEGA2560 Digital Pin 8
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRH
#define COOLANT_MIST_PORT PORTH
#define COOLANT_MIST_BIT 6 // MEGA2560 Digital Pin 9
#endif
// Define user-control pinouts (cycle start, reset, feed hold) input pins.
// NOTE: All pinouts pins must be on the same port and not on a port with other input pins (limits).
#define PINOUT_DDR DDRK
#define PINOUT_PIN PINK
#define PINOUT_PORT PORTK
#define PIN_RESET 0 // MEGA2560 Analog Pin 8
#define PIN_FEED_HOLD 1 // MEGA2560 Analog Pin 9
#define PIN_CYCLE_START 2 // MEGA2560 Analog Pin 10
#define PINOUT_INT PCIE2 // Pin change interrupt enable pin
#define PINOUT_INT_vect PCINT2_vect
#define PINOUT_PCMSK PCMSK2 // Pin change interrupt register
#define PINOUT_MASK ((1<<PIN_RESET)|(1<<PIN_FEED_HOLD)|(1<<PIN_CYCLE_START))
// Define probe switch input pin.
#define PROBE_DDR DDRK
#define PROBE_PIN PINK
#define PROBE_PORT PORTK
#define PROBE_BIT 3 // MEGA2560 Analog Pin 11
#define PROBE_MASK (1<<PROBE_BIT)
// Start of PWM & Stepper Enabled Spindle
#ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables
// Set Timer up to use TIMER2B which is attached to Digital Pin 9
#define TCCRA_REGISTER TCCR2A
#define TCCRB_REGISTER TCCR2B
#define OCR_REGISTER OCR2B
#define COMB_BIT COM2B1
#define WAVE0_REGISTER WGM20
#define WAVE1_REGISTER WGM21
#define WAVE2_REGISTER WGM22
#define WAVE3_REGISTER WGM23
#define SPINDLE_PWM_DDR DDRH
#define SPINDLE_PWM_PORT PORTH
#define SPINDLE_PWM_BIT 6 // MEGA2560 Digital Pin 9
#endif // End of VARIABLE_SPINDLE
#endif
//----------------------------------------------------------------------------------------
/*
#ifdef CPU_MAP_CUSTOM_PROC
// For a custom pin map or different processor, copy and paste one of the default cpu map
// settings above and modify it to your needs. Then, make sure the defined name is also
// changed in the config.h file.
#endif
*/
#endif

View File

@ -2,7 +2,7 @@
defaults.h - defaults settings configuration file
Part of Grbl
Copyright (c) 2012-2013 Sungeun K. Jeon
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
@ -32,29 +32,34 @@
#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_ARC_TOLERANCE 0.005 // mm
#define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min
#define DEFAULT_FEEDRATE 250.0
#define DEFAULT_ACCELERATION (10.0*60*60) // 10 mm/min^2
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_AUTO_START 1 // true
#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_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
#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
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_X_MAX_TRAVEL 200 // mm
#define DEFAULT_Y_MAX_TRAVEL 200 // mm
#define DEFAULT_Z_MAX_TRAVEL 200 // mm
#endif
#ifdef DEFAULTS_SHERLINE_5400
@ -66,29 +71,34 @@
#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
#define DEFAULT_Y_MAX_TRAVEL 125.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
#define DEFAULT_RAPID_FEEDRATE 635.0 // mm/min (25ipm)
#define DEFAULT_FEEDRATE 254.0 // mm/min (10ipm)
#define DEFAULT_ACCELERATION 50.0*60*60 // 50 mm/min^2
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_REPORT_INCHES 1 // false
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_REPORT_INCHES 0 // true
#define DEFAULT_AUTO_START 1 // true
#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_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
#define DEFAULT_HOMING_FEED_RATE 50.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 635.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_X_MAX_TRAVEL 200 // mm
#define DEFAULT_Y_MAX_TRAVEL 200 // mm
#define DEFAULT_Z_MAX_TRAVEL 200 // mm
#endif
#ifdef DEFAULTS_SHAPEOKO
@ -103,29 +113,76 @@
#define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
#define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
#define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z)
#define DEFAULT_X_MAX_RATE 1000.0 // mm/min
#define DEFAULT_Y_MAX_RATE 1000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 1000.0 // mm/min
#define DEFAULT_X_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2
#define DEFAULT_Y_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2
#define DEFAULT_Z_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 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_ARC_TOLERANCE 0.005 // mm
#define DEFAULT_RAPID_FEEDRATE 1000.0 // mm/min
#define DEFAULT_FEEDRATE 250.0
#define DEFAULT_ACCELERATION (15.0*60*60) // 15 mm/min^2
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_STEPPING_INVERT_MASK ((1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_AUTO_START 1 // true
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_SOFT_LIMIT_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_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min
#define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-255)
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_SHAPEOKO_2
// Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos
// grblShield with a 24V, 4.2A power supply.
#define MICROSTEPS_XY 8
#define STEP_REVS_XY 200
#define MM_PER_REV_XY (2.0*20) // 2mm belt pitch, 20 pulley teeth
#define MICROSTEPS_Z 2
#define STEP_REVS_Z 200
#define MM_PER_REV_Z 1.250 // 1.25 mm/rev leadscrew
#define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
#define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY)
#define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z)
#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 (25.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2
#define DEFAULT_Y_ACCELERATION (25.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2
#define DEFAULT_Z_ACCELERATION (25.0*60*60) // 25*60*60 mm/min^2 = 25 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<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_X_MAX_TRAVEL 200 // mm
#define DEFAULT_Y_MAX_TRAVEL 200 // mm
#define DEFAULT_Z_MAX_TRAVEL 200 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_AUTO_START 1 // true
#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_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#endif
#ifdef DEFAULTS_ZEN_TOOLWORKS_7x7
@ -138,29 +195,34 @@
#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 6000.0 // mm/min
#define DEFAULT_Y_MAX_RATE 6000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 6000.0 // mm/min
#define DEFAULT_X_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2
#define DEFAULT_Y_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2
#define DEFAULT_Z_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 190.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 180.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 150.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_ARC_TOLERANCE 0.005 // mm
#define DEFAULT_RAPID_FEEDRATE 2500.0 // mm/min
#define DEFAULT_FEEDRATE 1000.0 // mm/min
#define DEFAULT_ACCELERATION 150.0*60*60 // 150 mm/min^2
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_STEPPING_INVERT_MASK (1<<Y_DIRECTION_BIT)
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_DIRECTION_BIT))
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_AUTO_START 1 // true
#define DEFAULT_INVERT_ST_ENABLE 0 // false
#define DEFAULT_SOFT_LIMIT_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_HOMING_ENABLE 0 // false
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir
#define DEFAULT_HOMING_RAPID_FEEDRATE 500.0 // mm/min
#define DEFAULT_HOMING_FEEDRATE 50.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)
#define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 250.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)
#define DEFAULT_DECIMAL_PLACES 3
#define DEFAULT_X_MAX_TRAVEL 200 // mm
#define DEFAULT_Y_MAX_TRAVEL 200 // mm
#define DEFAULT_Z_MAX_TRAVEL 200 // mm
#endif
#endif

97
doc/pinmapping.txt Normal file
View File

@ -0,0 +1,97 @@
Mega328P Arduino Pin Mapping
============================
Digital 0 PD0 (RX)
Digital 1 PD1 (TX)
Digital 2 PD2
Digital 3 PD3
Digital 4 PD4
Digital 5 PD5
Digital 6 PD6
Digital 7 PD7
Digital 8 PB0
Digital 9 PB1
Digital 10 PB2
Digital 11 PB3 (MOSI)
Digital 12 PB4 (MISO)
Digital 13 PB5 (SCK)
Analog 0 PC0
Analog 1 PC1
Analog 2 PC2
Analog 3 PC3
Analog 4 PC4
Mega2560 Arduino Pin Mapping
============================
Digital pin 22 PA0 ( AD0 )
Digital pin 23 PA1 ( AD1 )
Digital pin 24 PA2 ( AD2 )
Digital pin 25 PA3 ( AD3 )
Digital pin 26 PA4 ( AD4 )
Digital pin 27 PA5 ( AD5 )
Digital pin 28 PA6 ( AD6 )
Digital pin 29 PA7 ( AD7 )
Digital pin 53 (PWM)(RX1) PB0 ( SS/PCINT0 )
Digital pin 52 (PWM)(SDA) PB1 ( SCK/PCINT1 )
Digital pin 51 (PWM)(SCL) PB2 ( MOSI/PCINT2 )
Digital pin 50 PB3 ( MISO/PCINT3 )
Digital pin 10 (PWM) PB4 ( OC2A/PCINT4 )
Digital pin 11 (PWM) PB5 ( OC1A/PCINT5 )
Digital pin 12 (PWM) PB6 ( OC1B/PCINT6 )
Digital pin 13 (PWM) PB7 ( OC0A/OC1C/PCINT7 )
Digital pin 37 PC0 ( A8 )
Digital pin 36 PC1 ( A9 )
Digital pin 35 PC2 ( A10 )
Digital pin 34 PC3 ( A11 )
Digital pin 33 PC4 ( A12 )
Digital pin 32 PC5 ( A13 )
Digital pin 31 PC6 ( A14 )
Digital pin 30 PC7 ( A15 )
Digital pin 21 (SCL) PD0 ( SCL/INT0 )
Digital pin 20 (SDA) PD1 ( SDA/INT1 )
Digital pin 19 PD2 ( RXDI/INT2 )
Digital pin 18 PD3 ( TXD1/INT3 )
Digital pin 38 PD7 ( T0 )
Digital pin 0 (PWM) (RX0) PE0 ( RXD0/PCINT8 )
Digital pin 1 (PWM) (TX0) PE1 ( TXD0 )
Digital pin 5 (PWM) PE3 ( OC3A/AIN1 )
Digital pin 2 (PWM) PE4 ( OC3B/INT4 )
Digital pin 3 (PWM) PE5 ( OC3C/INT5 )
Analog pin 0 PF0 ( ADC0 )
Analog pin 1 PF1 ( ADC1 )
Analog pin 2 PF2 ( ADC2 )
Analog pin 3 PF3 ( ADC3 )
Analog pin 4 PF4 ( ADC4/TMK )
Analog pin 5 PF5 ( ADC5/TMS )
Analog pin 6 PF6 ( ADC6/PCINT14 )
Analog pin 7 PF7 ( ADC7/PCINT15 )
Digital pin 41 PG0 ( WR )
Digital pin 40 PG1 ( RD )
Digital pin 39 PG2 ( ALE )
Digital pin 4 (PWM) PG5 ( OC0B )
Digital pin 17 (PWM) PH0 ( RXD2 )
Digital pin 16 (PWM) PH1 ( TXD2 )
Digital pin 6 (PWM)(RX3 ) PH3 ( OC4A )
Digital pin 7 (PWM)(TX2) PH4 ( OC4B )
Digital pin 8 (PWM)(RX2 ) PH5 ( OC4C )
Digital pin 9 (PWM)(TX1) PH6 ( OC2B )
Digital pin 15 PJ0 ( RXD3/PCINT9 )
Digital pin 14 PJ1 ( TXD3/PCINT10 )
Analog pin 8 PK0 ( ADC8/PCINT16 )
Analog pin 9 PK1 ( ADC9/PCINT17 )
Analog pin 10 PK2 ( ADC10/PCINT18 )
Analog pin 11 PK3 ( ADC11/PCINT19 )
Analog pin 12 PK4 ( ADC12/PCINT20 )
Analog pin 13 PK5 ( ADC13/PCINT21 )
Analog pin 14 PK6 ( ADC14/PCINT22 )
Analog pin 15 PK7 ( ADC15/PCINT23 )
Digital pin 49 PL0 ( ICP4 )
Digital pin 48 PL1 ( ICP5 )
Digital pin 47 PL2 ( T5 )
Digital pin 46 (PWM) PL3 ( OC5A )
Digital pin 45 (PWM) PL4 ( OC5B )
Digital pin 44 (PWM) PL5 ( OC5C )
Digital pin 43 PL6
Digital pin 42 PL7

437
doc/test/grbl_sim.m Normal file
View File

@ -0,0 +1,437 @@
% ----------------------------------------------------------------------------------------
% The MIT License (MIT)
%
% Copyright (c) 2014 Sungeun K. Jeon
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
% in the Software without restriction, including without limitation the rights
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the Software is
% furnished to do so, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included in
% all copies or substantial portions of the Software.
%
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
% THE SOFTWARE.
% ----------------------------------------------------------------------------------------
% This MATLAB script was written for the purpose of being a GRBL planner simulator. This
% simulator is a rough representation of the actual workings of Grbl on the Arduino, but
% was used to hone and proof the actual planner by providing quick visual feedback on its
% functionality when experimented on. This script should be considered for educational
% purposes only. This script requires and executes a pre-parsed g-code file from the
% matlab_convert.py script that is in a specific non-g-code format.
% There will be two figures plotted. The first is the line motion paths of the complete
% g-code program. The second is a representation of Grbl's planner buffer as new line
% motions are fed to it, plotting the velocity profiles the stepper motors will execute.
% Every time the user inputs an <Enter>, this feeds the simulator planner one line motion
% block. The left side is the first block in the buffer and the one that will be executed
% by the stepper module first. The right side is the end of the planner buffer, where the
% most recent streamed block is appended onto the planner buffer. Grbl's planner
% optimizes the velocity profiles between the beginning and end of the buffer based on
% the acceleration limits, intended velocity/feedrate, and line motion junction angles
% with their corresponding velocity limits (i.e. junctions with acute angles needs to come
% to a complete stop vs straight junctions can continue through at full speed.)
% ----------------------------------------------------------------------------------------
% Main function
% NOTE: This is just a way to keep all functions in one place, but all non-global variables
% are cleared as soon as this script completes.
function main()
% Load pre-parsed gcode moves.
close all;
warning off;
clearvars -global
fid = fopen('matlab.gcode','r');
gcode = textscan(fid,'%d8%f32%f32%f32%f32');
nblock = length(gcode{1});
% Plot all g-code moves.
figure
line(gcode{3},gcode{4},gcode{5});
axis equal;
% axis([min(gcode{3}) max(gcode{3}) min(gcode{4}) max(gcode{4}) min(gcode{5}) max(gcode{5})]);
title('G-code programming line motions');
view(3);
% Set up figure for planner queue
figure
% Print help.
disp('<NOTE: Press Enter to Advance One G-Code Line Motion>');
disp(' BLUE line indicates completed planner blocks that require no recalculation.');
disp(' RED line indicates planner blocks that have been recalculated.');
disp(' GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block.');
disp(' BLACK dotted-line and ''x'' indicates block nominal speed and max junction velocity, respectively.');
disp(' CYAN ''.'' indicates block initial entry speed.');
% Define Grbl settings.
BUFFER_SIZE = 18; % Number of planner blocks in its ring buffer.
steps_per_mm = 200;
seekrate = 2500; % mm/min
acceleration = [100 100 100]; % mm/sec^2 [ X Y Z ] axes
junction_deviation = 0.1; % mm. See Grbl documentation on this parameter.
inch_2_mm = 25.4;
ACCELERATION_TICKS_PER_SECOND = 100;
gcode{2} = gcode{2};
gcode{2} = inch_2_mm*gcode{2};
gcode{3} = inch_2_mm*gcode{3};
gcode{4} = inch_2_mm*gcode{4};
gcode{5} = inch_2_mm*gcode{5};
% Initialize blocks
block.steps = [];
block.step_event_count = [];
block.delta_mm = [];
block.millimeters = [];
block.acceleration = [];
block.speed = [];
block.nominal_speed = [];
block.max_entry_speed = [];
block.entry_speed = [];
block.recalculate_flag = false;
for i = 2:BUFFER_SIZE
block(i) = block(1);
end
% Initialize planner
position = [0 0 0];
prev_unit_vec = [0 0 0];
previous_nominal_speed = 0;
pos = 0;
% BHEAD and BTAIL act as pointers to the block head and tail.
% BPLANNED acts as a pointer of the location of the end of a completed/optimized plan.
bhead = 1;
btail = 1;
bplanned = 1;
global block bhead btail bplanned nind acceleration BUFFER_SIZE pos ACCELERATION_TICKS_PER_SECOND
% Main loop. Simulates plan_buffer_line(). All of the precalculations for the newest incoming
% block occurs here. Anything independent of the planner changes.
for i = 1:nblock
target = round([gcode{3}(i) gcode{4}(i) gcode{5}(i)].*steps_per_mm);
if gcode{1}(i) == 1
feedrate = gcode{2}(i);
else
feedrate = seekrate;
end
nind = next_block_index(bhead);
if nind == btail
% Simulate a constantly full buffer. Move buffer tail.
bind = next_block_index(btail);
% Push planned pointer if encountered. Prevents it from looping back around the ring buffer.
if btail == bplanned; bplanned = bind; end
btail = bind;
end
block(bhead).steps = abs(target-position);
block(bhead).step_event_count = max(block(bhead).steps);
% Bail if this is a zero-length block
if block(bhead).step_event_count == 0
disp(['Zero-length block in line ',int2str(i)]);
else
% Compute path vector in terms of absolute step target and current positions
delta_mm = single((target-position)./steps_per_mm);
block(bhead).millimeters = single(norm(delta_mm));
inverse_millimeters = single(1/block(bhead).millimeters);
% Compute path unit vector
unit_vec = delta_mm/block(bhead).millimeters;
% Calculate speed in mm/minute for each axis
inverse_minute = single(feedrate * inverse_millimeters);
block(bhead).speed = delta_mm*inverse_minute;
block(bhead).nominal_speed = block(bhead).millimeters*inverse_minute;
% Calculate block acceleration. Operates on absolute value of unit vector.
[max_acc,ind] = max(abs(unit_vec)./acceleration); % Determine limiting acceleration
block(bhead).acceleration = acceleration(ind)/abs(unit_vec(ind));
% Compute maximum junction speed
block(bhead).max_entry_speed = 0.0;
if previous_nominal_speed > 0.0
cos_theta = dot(-previous_unit_vec,unit_vec);
if (cos_theta < 0.95)
block(bhead).max_entry_speed = min([block(bhead).nominal_speed,previous_nominal_speed]);
if (cos_theta > -0.95)
sin_theta_d2 = sqrt(0.5*(1.0-cos_theta));
block(bhead).max_entry_speed = min([block(bhead).max_entry_speed,sqrt(block(bhead).acceleration*3600*junction_deviation*sin_theta_d2/(1.0-sin_theta_d2))]);
end
end
end
block(bhead).entry_speed = 0; % Just initialize. Set accurately in the replanning function.
block(bhead).recalculate_flag = true; % Plotting flag to indicate this block has been updated.
previous_unit_vec = unit_vec;
previous_nominal_speed = block(bhead).nominal_speed;
position = target;
bhead = nind; % Block complete. Push buffer pointer.
planner_recalculate();
plot_buffer_velocities();
end
end
return
% Computes the next block index in the planner ring buffer
function block_index = next_block_index(block_index)
global BUFFER_SIZE
block_index = block_index + 1;
if block_index > BUFFER_SIZE
block_index = 1;
end
return
% Computes the previous block index in the planner ring buffer
function block_index = prev_block_index(block_index)
global BUFFER_SIZE
block_index = block_index-1;
if block_index < 1
block_index = BUFFER_SIZE;
end
return
% Planner recalculate function. The magic happens here.
function planner_recalculate(block)
global block bhead btail bplanned acceleration
bind = prev_block_index(bhead);
if bind == bplanned; return; end % Bail, if only one block in buffer. Can't be operated on.
% 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.
next = [];
curr = bind; % Last block in buffer.
% Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
block(curr).entry_speed = min([block(curr).max_entry_speed,sqrt(2*block(curr).acceleration*60*60*block(curr).millimeters)]);
bind = prev_block_index(bind); % Btail or second to last block
if (bind == bplanned)
% Only two plannable blocks in buffer. Reverse pass complete.
% Check if the first block is the tail. If so, notify stepper module to update its current parameters.
% if bind == btail; update_tail_block; end
else
% Three or more plannable blocks in buffer. Loop it.
while bind ~= bplanned % Loop until bplanned point hits. Replans to last plan point.
next = curr;
curr = bind;
bind = prev_block_index( bind ); % Previous block pointer.
% Check if the first block is the tail. If so, notify stepper module to update its current parameters.
% if bind == btail; update_tail_block; end
% Compute maximum entry speed decelerating over the current block from its exit speed.
if block(curr).entry_speed ~= block(curr).max_entry_speed
block(curr).recalculate_flag = true; % Plotting flag to indicate this block has been updated.
block(curr).entry_speed = min([ block(curr).max_entry_speed,...
sqrt(block(next).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters)]);
end
end
end
% For two blocks, reverse pass is skipped, but forward pass plans second block entry speed
% onward. This prevents the first, or the potentially executing block, from being over-written.
% NOTE: Can never be bhead, since bsafe is always in active buffer.
next = bplanned;
bind = next_block_index(bplanned); % Start at bplanned
while bind ~= bhead
curr = next;
next = bind;
% An acceleration block is always an optimally planned block since it starts from the first
% block's current speed or a maximum junction speed. Compute accelerations from this block
% and update the next block's entry speed.
if (block(curr).entry_speed < block(next).entry_speed)
% Once speed is set by forward planner, the plan for this block is finished and optimal.
% Increment the planner pointer forward one block.
entry_speed = sqrt(block(curr).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters);
if (block(next).entry_speed > entry_speed)
block(next).entry_speed = entry_speed;
bplanned = bind;
end
end
% Check if the next block entry speed is at max_entry_speed. If so, move the planned pointer, since
% this entry speed cannot be improved anymore and all prior blocks have been completed and optimally planned.
if block(next).entry_speed == block(next).max_entry_speed
bplanned = bind;
end
% Recalculate trapezoid can be installed here, since it scans through all of the plannable blocks.
% NOTE: Eventually this will only be computed when being executed.
bind = next_block_index( bind );
end
return
% ----------------------------------------------------------------------------------------
% PLOTTING FUNCTIONS
% Plots the entire buffer plan into a MATLAB figure to visual the plan.
% BLUE line indicates completed planner blocks that require no recalculation.
% RED line indicates planner blocks that have been recalculated.
% GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block.
% BLACK dotted-line and 'x' indicates block nominal speed and max junction velocity, respectively.
% CYAN '.' indicates block initial entry speed.
function plot_buffer_velocities()
global block bhead btail bplanned acceleration pos ACCELERATION_TICKS_PER_SECOND
bind = btail;
curr = [];
next = [];
pos_initial = 0;
pos = 0;
while bind ~= bhead
curr = next;
next = bind;
hold on;
if ~isempty(curr)
accel_d = estimate_acceleration_distance(block(curr).entry_speed, block(curr).nominal_speed, block(curr).acceleration*60*60);
decel_d = estimate_acceleration_distance(block(curr).nominal_speed, block(next).entry_speed,-block(curr).acceleration*60*60);
plateau_d = block(curr).millimeters-accel_d-decel_d;
if plateau_d < 0
accel_d = intersection_distance(block(curr).entry_speed, block(next).entry_speed, block(curr).acceleration*60*60, block(curr).millimeters);
if accel_d < 0
accel_d = 0;
elseif accel_d > block(curr).millimeters
accel_d = block(curr).millimeters;
end
plateau_d = 0;
end
color = 'b';
if (block(curr).recalculate_flag || block(next).recalculate_flag)
block(curr).recalculate_flag = false;
color = 'r';
end
if bplanned == curr
color = 'g';
end
plot_trap(pos,block(curr).entry_speed,block(next).entry_speed,block(curr).nominal_speed,block(curr).acceleration,accel_d,plateau_d,block(curr).millimeters,color)
plot([pos pos+block(curr).millimeters],block(curr).nominal_speed*[1 1],'k:') % BLACK dotted indicates
plot(pos,block(curr).max_entry_speed,'kx')
pos = pos + block(curr).millimeters;
plot(pos,block(next).entry_speed,'c.');
end
bind = next_block_index( bind );
end
accel_d = estimate_acceleration_distance(block(next).entry_speed, block(next).nominal_speed, block(next).acceleration*60*60);
decel_d = estimate_acceleration_distance(block(next).nominal_speed, 0, -block(next).acceleration*60*60);
plateau_d = block(next).millimeters-accel_d-decel_d;
if plateau_d < 0
accel_d = intersection_distance(block(next).entry_speed, 0, block(next).acceleration*60*60, block(next).millimeters);
if accel_d < 0
accel_d = 0;
elseif accel_d > block(next).millimeters
accel_d = block(next).millimeters;
end
plateau_d = 0;
end
block(next).recalculate_flag = false;
color = 'r';
if bplanned == next
color= 'g';
end
plot_trap(pos,block(next).entry_speed,0,block(next).nominal_speed,block(next).acceleration,accel_d,plateau_d,block(next).millimeters,color)
plot([pos pos+block(next).millimeters],block(next).nominal_speed*[1 1],'k:')
plot(pos,block(next).max_entry_speed,'kx')
plot(pos,block(next).entry_speed,'.');
pos = pos + block(next).millimeters;
plot(pos,0,'rx');
xlabel('mm');
ylabel('mm/sec');
xlim([pos_initial pos])
title('Planner buffer optimized velocity profile');
pause();
hold off;
plot(pos,0)
return
function d_a = estimate_acceleration_distance(initial_rate, target_rate, acceleration,rate_delta)
d_a = (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration);
return
function d_i = intersection_distance(initial_rate, final_rate, acceleration, distance, rate_delta)
d_i = (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration);
return
% Simply plots the ac/de-celeration curves and plateaus of a trapezoid.
function plot_trap(pos,initial_rate,final_rate,rate,accel,accel_d,plateau_d,millimeters,color)
dx = 1.0; % Line segment length
linex = [pos]; liney = [initial_rate];
% Acceleration
np = floor(accel_d/dx);
if np
v = initial_rate;
for i = 1:np
v = sqrt(v^2+2*accel*60*60*dx);
linex = [linex pos+i*dx];
liney = [liney v];
end
end
% Plateau
v = sqrt(initial_rate^2 + 2*accel*60*60*accel_d);
if v < rate
rate = v;
end
linex = [linex pos+[accel_d accel_d+plateau_d]];
liney = [liney [rate rate]];
% Deceleration
np = floor((millimeters-accel_d-plateau_d)/dx);
if np
v = rate;
for i = 1:np
v = sqrt(v^2-2*accel*60*60*dx);
linex = [linex pos+i*dx+accel_d+plateau_d];
liney = [liney v];
end
end
linex = [linex pos+millimeters];
liney = [ liney final_rate];
plot(linex,liney,color);
return

2362
doc/test/matlab.gcode Normal file

File diff suppressed because it is too large Load Diff

270
doc/test/matlab_convert.py Executable file
View File

@ -0,0 +1,270 @@
#!/usr/bin/env python
"""\
The MIT License (MIT)
Copyright (c) 2014 Sungeun K. Jeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
----------------------------------------------------------------------------------------
"""
"""\
G-code preprocessor for the grbl_sim.m MATLAB script. Parses the g-code program to a
specific file format for the MATLAB script to use. Based on PreGrbl by @chamnit.
How to use: When running this python script, it will process the g-code program under
the filename "test.gcode" (may be changed below) and produces a file called "matlab.gcode"
that the grbl_sim.m MATLAB script will search for and execute.
"""
import re
from math import *
from copy import *
# -= SETTINGS =-
filein = 'test.gcode' # Input file name
fileout = 'matlab.gcode' # Output file name
ndigits_in = 4 # inch significant digits after '.'
ndigits_mm = 2 # mm significant digits after '.'
# mm_per_arc_segment = 0.38 # mm per arc segment
arc_tolerance = 0.00005*25.4
n_arc_correction = 20
inch2mm = 25.4 # inch to mm conversion scalar
verbose = False # Verbose flag to show all progress
remove_unsupported = True # Removal flag for all unsupported statements
# Initialize parser state
gc = { 'current_xyz' : [0,0,0],
'feed_rate' : 0, # F0
'motion_mode' : 'SEEK', # G00
'plane_axis' : [0,1,2], # G17
'inches_mode' : False, # G21
'inverse_feedrate_mode' : False, # G94
'absolute_mode' : True} # G90
def unit_conv(val) : # Converts value to mm
if gc['inches_mode'] : val *= inch2mm
return(val)
def fout_conv(val) : # Returns converted value as rounded string for output file.
if gc['inches_mode'] : return( str(round(val/inch2mm,ndigits_in)) )
else : return( str(round(val,ndigits_mm)) )
# Open g-code file
fin = open(filein,'r');
fout = open(fileout,'w');
# Iterate through g-code file
l_count = 0
for line in fin:
l_count += 1 # Iterate line counter
# Strip comments/spaces/tabs/new line and capitalize. Comment MSG not supported.
block = re.sub('\s|\(.*?\)','',line).upper()
block = re.sub('\\\\','',block) # Strip \ block delete character
block = re.sub('%','',block) # Strip % program start/stop character
if len(block) == 0 : # Ignore empty blocks
print "Skipping: " + line.strip()
else : # Process valid g-code clean block. Assumes no block delete characters or comments
g_cmd = re.findall(r'[^0-9\.\-]+',block) # Extract block command characters
g_num = re.findall(r'[0-9\.\-]+',block) # Extract block numbers
# G-code block error checks
# if len(g_cmd) != len(g_num) : print block; raise Exception('Invalid block. Unbalanced word and values.')
if 'N' in g_cmd :
if g_cmd[0]!='N': raise Exception('Line number must be first command in line.')
if g_cmd.count('N') > 1: raise Exception('More than one line number in block.')
g_cmd = g_cmd[1:] # Remove line number word
g_num = g_num[1:]
# Block item repeat checks? (0<=n'M'<5, G/M modal groups)
# Initialize block state
blk = { 'next_action' : 'DEFAULT',
'absolute_override' : False,
'target_xyz' : deepcopy(gc['current_xyz']),
'offset_ijk' : [0,0,0],
'radius_mode' : False,
'unsupported': [] }
# Pass 1
for cmd,num in zip(g_cmd,g_num) :
fnum = float(num)
inum = int(fnum)
if cmd is 'G' :
if inum is 0 : gc['motion_mode'] = 'SEEK'
elif inum is 1 : gc['motion_mode'] = 'LINEAR'
elif inum is 2 : gc['motion_mode'] = 'CW_ARC'
elif inum is 3 : gc['motion_mode'] = 'CCW_ARC'
elif inum is 4 : blk['next_action'] = 'DWELL'
elif inum is 17 : gc['plane_axis'] = [0,1,2] # Select XY Plane
elif inum is 18 : gc['plane_axis'] = [0,2,1] # Select XZ Plane
elif inum is 19 : gc['plane_axis'] = [1,2,0] # Select YZ Plane
elif inum is 20 : gc['inches_mode'] = True
elif inum is 21 : gc['inches_mode'] = False
elif inum in [28,30] : blk['next_action'] = 'GO_HOME'
elif inum is 53 : blk['absolute_override'] = True
elif inum is 54 : pass
elif inum is 80 : gc['motion_mode'] = 'MOTION_CANCEL'
elif inum is 90 : gc['absolute_mode'] = True
elif inum is 91 : gc['absolute_mode'] = False
elif inum is 92 : blk['next_action'] = 'SET_OFFSET'
elif inum is 93 : gc['inverse_feedrate_mode'] = True
elif inum is 94 : gc['inverse_feedrate_mode'] = False
else :
print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count)
if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num)))
elif cmd is 'M' :
if inum in [0,1] : pass # Program Pause
elif inum in [2,30,60] : pass # Program Completed
elif inum is 3 : pass # Spindle Direction 1
elif inum is 4 : pass # Spindle Direction -1
elif inum is 5 : pass # Spindle Direction 0
else :
print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count)
if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num)))
elif cmd is 'T' : pass # Tool Number
# Pass 2
for cmd,num in zip(g_cmd,g_num) :
fnum = float(num)
if cmd is 'F' : gc['feed_rate'] = unit_conv(fnum) # Feed Rate
elif cmd in ['I','J','K'] : blk['offset_ijk'][ord(cmd)-ord('I')] = unit_conv(fnum) # Arc Center Offset
elif cmd is 'N' : pass
elif cmd is 'P' : p = fnum # Misc value parameter
elif cmd is 'R' : r = unit_conv(fnum); blk['radius_mode'] = True # Arc Radius Mode
elif cmd is 'S' : pass # Spindle Speed
elif cmd in ['X','Y','Z'] : # Target Coordinates
if (gc['absolute_mode'] | blk['absolute_override']) :
blk['target_xyz'][ord(cmd)-ord('X')] = unit_conv(fnum)
else :
blk['target_xyz'][ord(cmd)-ord('X')] += unit_conv(fnum)
# Execute actions
if blk['next_action'] is 'GO_HOME' :
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
elif blk['next_action'] is 'SET_OFFSET' :
pass
elif blk['next_action'] is 'DWELL' :
if p < 0 : raise Exception('Dwell time negative.')
else : # 'DEFAULT'
if gc['motion_mode'] is 'SEEK' :
fout.write('0 '+fout_conv(gc['feed_rate']))
fout.write(' '+fout_conv(blk['target_xyz'][0]))
fout.write(' '+fout_conv(blk['target_xyz'][1]))
fout.write(' '+fout_conv(blk['target_xyz'][2]))
fout.write('\n')
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
elif gc['motion_mode'] is 'LINEAR' :
fout.write('1 '+fout_conv(gc['feed_rate']))
fout.write(' '+fout_conv(blk['target_xyz'][0]))
fout.write(' '+fout_conv(blk['target_xyz'][1]))
fout.write(' '+fout_conv(blk['target_xyz'][2]))
fout.write('\n')
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
elif gc['motion_mode'] in ['CW_ARC','CCW_ARC'] :
axis = gc['plane_axis']
# Convert radius mode to ijk mode
if blk['radius_mode'] :
x = blk['target_xyz'][axis[0]]-gc['current_xyz'][axis[0]]
y = blk['target_xyz'][axis[1]]-gc['current_xyz'][axis[1]]
if not (x==0 and y==0) : raise Exception('Same target and current XYZ not allowed in arc radius mode.')
h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y)
if isnan(h_x2_div_d) : raise Exception('Floating point error in arc conversion')
if gc['motion_mode'] is 'CCW_ARC' : h_x2_div_d = -h_x2_div_d
if r < 0 : h_x2_div_d = -h_x2_div_d
blk['offset_ijk'][axis[0]] = (x-(y*h_x2_div_d))/2;
blk['offset_ijk'][axis[1]] = (y+(x*h_x2_div_d))/2;
else :
radius = sqrt(blk['offset_ijk'][axis[0]]**2+blk['offset_ijk'][axis[1]]**2)
center_axis0 = gc['current_xyz'][axis[0]]+blk['offset_ijk'][axis[0]]
center_axis1 = gc['current_xyz'][axis[1]]+blk['offset_ijk'][axis[1]]
linear_travel = blk['target_xyz'][axis[2]]-gc['current_xyz'][axis[2]]
r_axis0 = -blk['offset_ijk'][axis[0]]
r_axis1 = -blk['offset_ijk'][axis[1]]
rt_axis0 = blk['target_xyz'][axis[0]] - center_axis0;
rt_axis1 = blk['target_xyz'][axis[1]] - center_axis1;
clockwise_sign = 1
if gc['motion_mode'] is 'CW_ARC' : clockwise_sign = -1
angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1)
if gc['motion_mode'] is 'CW_ARC' :
if angular_travel >= 0 :
angular_travel -= 2*pi
else :
if angular_travel <= 0 :
angular_travel += 2*pi
millimeters_of_travel = sqrt((angular_travel*radius)**2 + abs(linear_travel)**2)
mm_per_arc_segment = sqrt(4*(2*radius*arc_tolerance-arc_tolerance**2))
segments = int(millimeters_of_travel/mm_per_arc_segment)
print segments
print l_count
theta_per_segment = angular_travel/segments
linear_per_segment = linear_travel/segments
cos_T = 1-0.5*theta_per_segment*theta_per_segment
sin_T = theta_per_segment-theta_per_segment**3/6
print(fout_conv(mm_per_arc_segment))
print theta_per_segment*180/pi
arc_target = [0,0,0]
arc_target[axis[2]] = gc['current_xyz'][axis[2]]
count = 0
for i in range(1,segments+1) :
if i < segments :
if count < n_arc_correction :
r_axisi = r_axis0*sin_T + r_axis1*cos_T
r_axis0 = r_axis0*cos_T - r_axis1*sin_T
r_axis1 = deepcopy(r_axisi)
count += 1
else :
cos_Ti = cos((i-1)*theta_per_segment)
sin_Ti = sin((i-1)*theta_per_segment)
print n_arc_correction*(r_axis0 -( -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti))
print n_arc_correction*(r_axis1 -( -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti))
cos_Ti = cos(i*theta_per_segment)
sin_Ti = sin(i*theta_per_segment)
r_axis0 = -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti
r_axis1 = -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti
count = 0
arc_target[axis[0]] = center_axis0 + r_axis0
arc_target[axis[1]] = center_axis1 + r_axis1
arc_target[axis[2]] += linear_per_segment
else :
arc_target = deepcopy(blk['target_xyz']) # Last segment at target_xyz
# Write only changed variables.
fout.write('1 '+fout_conv(gc['feed_rate']))
fout.write(' '+fout_conv(arc_target[0]))
fout.write(' '+fout_conv(arc_target[1]))
fout.write(' '+fout_conv(arc_target[2]))
fout.write('\n')
gc['current_xyz'] = deepcopy(arc_target) # Update position
print 'Done!'
# Close files
fin.close()
fout.close()

2363
doc/test/test.gcode Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#ifndef eeprom_h
#define eeprom_h
char eeprom_get_char(unsigned int addr);
unsigned char eeprom_get_char(unsigned int addr);
void eeprom_put_char(unsigned int addr, unsigned char new_value);
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size);
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size);

1349
gcode.c

File diff suppressed because it is too large Load Diff

190
gcode.h
View File

@ -2,8 +2,8 @@
gcode.h - rs274/ngc parser.
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,70 +21,166 @@
#ifndef gcode_h
#define gcode_h
#include <avr/io.h>
#include "nuts_bolts.h"
#include "system.h"
// Define modal group internal numbers for checking multiple command violations and tracking the
// type of command that is called in the block. A modal group is a group of g-code commands that are
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
#define MODAL_GROUP_NONE 0
#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal
#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion
#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection
#define MODAL_GROUP_3 4 // [G90,G91] Distance mode
#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping
#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode
#define MODAL_GROUP_6 7 // [G20,G21] Units
#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning
#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
// 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_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 command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute.
#define MOTION_MODE_SEEK 0 // G0
#define MOTION_MODE_LINEAR 1 // G1
#define MOTION_MODE_CW_ARC 2 // G2
#define MOTION_MODE_CCW_ARC 3 // G3
#define MOTION_MODE_CANCEL 4 // G80
#define PROGRAM_FLOW_RUNNING 0
#define PROGRAM_FLOW_PAUSED 1 // M0, M1
#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
#define NON_MODAL_NONE 0
// 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_SET_COORDINATE_OFFSET 7 // G92
#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.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
// 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
// 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
// Modal Group G3: Distance mode
#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero)
#define DISTANCE_MODE_INCREMENTAL 1 // G91
// 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
// 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
// Modal Group G6: Units mode
#define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
#define UNITS_MODE_INCHES 1 // G20
// 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
// 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
// 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 G12: Active work coordinate system
// N/A: Stores coordinate system value (54-59) to change to.
#define WORD_F 0
#define WORD_I 1
#define WORD_J 2
#define WORD_K 3
#define WORD_L 4
#define WORD_N 5
#define WORD_P 6
#define WORD_R 7
#define WORD_S 8
#define WORD_T 9
#define WORD_X 10
#define WORD_Y 11
#define WORD_Z 12
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
uint8_t motion; // {G0,G1,G2,G3,G80}
uint8_t 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;
typedef struct {
uint8_t status_code; // Parser status for current block
uint8_t motion_mode; // {G0, G1, G2, G3, G80}
uint8_t inverse_feed_rate_mode; // {G93, G94}
uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21}
uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91}
uint8_t program_flow; // {M0, M1, M2, M30}
int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5}
uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9}
float feed_rate; // Millimeters/min
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
uint8_t tool;
// uint16_t spindle_speed; // RPM/100
uint8_t plane_axis_0,
plane_axis_1,
plane_axis_2; // The axes of the selected plane
uint8_t coord_select; // Active work coordinate system number. Default: 0=G54.
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
// position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
float f; // Feed
float ijk[3]; // 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
// float q; // G82 peck drilling
float r; // Arc radius
float s; // Spindle speed
// uint8_t t; // Tool selection
float xyz[3]; // 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.
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.
} parser_state_t;
extern parser_state_t gc;
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;
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();
@ -93,6 +189,6 @@ void gc_init();
uint8_t gc_execute_line(char *line);
// Set g-code parser position. Input in steps.
void gc_set_current_position(int32_t x, int32_t y, int32_t z);
void gc_sync_position();
#endif

392
limits.c
View File

@ -2,8 +2,8 @@
limits.c - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2012-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2012-2013 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
@ -19,52 +19,65 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "stepper.h"
#include "system.h"
#include "settings.h"
#include "nuts_bolts.h"
#include "config.h"
#include "spindle_control.h"
#include "motion_control.h"
#include "planner.h"
#include "protocol.h"
#include "planner.h"
#include "stepper.h"
#include "motion_control.h"
#include "limits.h"
#include "report.h"
#define MICROSECONDS_PER_ACCELERATION_TICK (1000000/ACCELERATION_TICKS_PER_SECOND)
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Axis search distance multiplier. Must be > 1.
void limits_init()
{
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) {
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
} else {
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
}
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
} else {
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable
PCICR &= ~(1 << LIMIT_INT);
limits_disable();
}
#ifdef ENABLE_SOFTWARE_DEBOUNCE
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDP0); // Set time-out at ~32msec.
#endif
}
void limits_disable()
{
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt
PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
}
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
// If a switch is triggered at all, something bad has happened and treat it as such, regardless
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
// bouncing pin without a debouncing method.
// bouncing pin without a debouncing method. A simple software debouncing feature may be enabled
// through the config.h file, where an extra timer delays the limit pin read by several milli-
// seconds to help with, not fix, bouncing switches.
// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
// homing cycles and will not respond correctly. Upon user request or need, there may be a
// special pinout for an e-stop, but it is generally recommended to just directly connect
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
ISR(LIMIT_INT_vect)
#ifndef ENABLE_SOFTWARE_DEBOUNCE
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{
// TODO: This interrupt may be used to manage the homing cycle directly with the main stepper
// interrupt without adding too much to it. All it would need is some way to stop one axis
// when its limit is triggered and continue the others. This may reduce some of the code, but
// would make Grbl a little harder to read and understand down road. Holding off on this until
// we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it.
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
@ -73,194 +86,191 @@ ISR(LIMIT_INT_vect)
if (sys.state != STATE_ALARM) {
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
mc_reset(); // Initiate system kill.
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate hard limit critical event
}
}
}
// Moves all specified axes in same specified direction (positive=true, negative=false)
// and at the homing rate. Homing is a special motion case, where there is only an
// acceleration followed by abrupt asynchronous stops by each axes reaching their limit
// switch independently. Instead of shoehorning homing cycles into the main stepper
// algorithm and overcomplicate things, a stripped-down, lite version of the stepper
// algorithm is written here. This also lets users hack and tune this code freely for
// their own particular needs without affecting the rest of Grbl.
// NOTE: Only the abort runtime command can interrupt this process.
static void homing_cycle(uint8_t cycle_mask, int8_t pos_dir, bool invert_pin, float homing_rate)
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(WDT_vect) // Watchdog timer ISR
{
// Determine governing axes with finest step resolution per distance for the Bresenham
// algorithm. This solves the issue when homing multiple axes that have different
// resolutions without exceeding system acceleration setting. It doesn't have to be
// perfect since homing locates machine zero, but should create for a more consistent
// and speedy homing routine.
// NOTE: For each axes enabled, the following calculations assume they physically move
// an equal distance over each time step until they hit a limit switch, aka dogleg.
uint32_t step_event_count, steps[N_AXIS];
uint8_t i, dist = 0;
clear_vector(steps);
for (i=0; i<N_AXIS; i++) {
if (cycle_mask & (1<<i)) {
dist++;
steps[i] = lround(settings.steps_per_mm[i]);
}
}
step_event_count = max(steps[X_AXIS], max(steps[Y_AXIS], steps[Z_AXIS]));
// To ensure global acceleration is not exceeded, reduce the governing axes nominal rate
// by adjusting the actual axes distance traveled per step. This is the same procedure
// used in the main planner to account for distance traveled when moving multiple axes.
// NOTE: When axis acceleration independence is installed, this will be updated to move
// all axes at their maximum acceleration and rate.
float ds = step_event_count/sqrt(dist);
// Compute the adjusted step rate change with each acceleration tick. (in step/min/acceleration_tick)
uint32_t delta_rate = ceil( ds*settings.acceleration[X_AXIS]/(60*ACCELERATION_TICKS_PER_SECOND));
#ifdef HOMING_RATE_ADJUST
// Adjust homing rate so a multiple axes moves all at the homing rate independently.
homing_rate *= sqrt(dist); // Eq. only works if axes values are 1 or 0.
#endif
// Nominal and initial time increment per step. Nominal should always be greater then 3
// usec, since they are based on the same parameters as the main stepper routine. Initial
// is based on the MINIMUM_STEPS_PER_MINUTE config. Since homing feed can be very slow,
// disable acceleration when rates are below MINIMUM_STEPS_PER_MINUTE.
uint32_t dt_min = lround(1000000*60/(ds*homing_rate)); // Cruising (usec/step)
uint32_t dt = 1000000*60/MINIMUM_STEPS_PER_MINUTE; // Initial (usec/step)
if (dt > dt_min) { dt = dt_min; } // Disable acceleration for very slow rates.
// Set default out_bits.
uint8_t out_bits0 = settings.invert_mask;
out_bits0 ^= (settings.homing_dir_mask & DIRECTION_MASK); // Apply homing direction settings
if (!pos_dir) { out_bits0 ^= DIRECTION_MASK; } // Invert bits, if negative dir.
// Initialize stepping variables
int32_t counter_x = -(step_event_count >> 1); // Bresenham counters
int32_t counter_y = counter_x;
int32_t counter_z = counter_x;
uint32_t step_delay = dt-settings.pulse_microseconds; // Step delay after pulse
uint32_t step_rate = 0; // Tracks step rate. Initialized from 0 rate. (in step/min)
uint32_t trap_counter = MICROSECONDS_PER_ACCELERATION_TICK/2; // Acceleration trapezoid counter
uint8_t out_bits;
uint8_t limit_state;
for(;;) {
// Reset out bits. Both direction and step pins appropriately inverted and set.
out_bits = out_bits0;
// Get limit pin state.
limit_state = LIMIT_PIN;
if (invert_pin) { limit_state ^= LIMIT_MASK; } // If leaving switch, invert to move.
// Set step pins by Bresenham line algorithm. If limit switch reached, disable and
// flag for completion.
if (cycle_mask & (1<<X_AXIS)) {
counter_x += steps[X_AXIS];
if (counter_x > 0) {
if (limit_state & (1<<X_LIMIT_BIT)) { out_bits ^= (1<<X_STEP_BIT); }
else { cycle_mask &= ~(1<<X_AXIS); }
counter_x -= step_event_count;
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (bit_isfalse(sys.execute,EXEC_ALARM)) {
uint8_t bits = LIMIT_PIN;
// Check limit pin state.
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { bits ^= LIMIT_MASK; }
if (bits & LIMIT_MASK) {
mc_reset(); // Initiate system kill.
bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate hard limit critical event
}
}
if (cycle_mask & (1<<Y_AXIS)) {
counter_y += steps[Y_AXIS];
if (counter_y > 0) {
if (limit_state & (1<<Y_LIMIT_BIT)) { out_bits ^= (1<<Y_STEP_BIT); }
else { cycle_mask &= ~(1<<Y_AXIS); }
counter_y -= step_event_count;
}
}
if (cycle_mask & (1<<Z_AXIS)) {
counter_z += steps[Z_AXIS];
if (counter_z > 0) {
if (limit_state & (1<<Z_LIMIT_BIT)) { out_bits ^= (1<<Z_STEP_BIT); }
else { cycle_mask &= ~(1<<Z_AXIS); }
counter_z -= step_event_count;
}
}
// Check if we are done or for system abort
if (!(cycle_mask) || (sys.execute & EXEC_RESET)) { return; }
// Perform step.
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (out_bits & STEP_MASK);
delay_us(settings.pulse_microseconds);
STEPPING_PORT = out_bits0;
delay_us(step_delay);
// Track and set the next step delay, if required. This routine uses another Bresenham
// line algorithm to follow the constant acceleration line in the velocity and time
// domain. This is a lite version of the same routine used in the main stepper program.
if (dt > dt_min) { // Unless cruising, check for time update.
trap_counter += dt; // Track time passed since last update.
if (trap_counter > MICROSECONDS_PER_ACCELERATION_TICK) {
trap_counter -= MICROSECONDS_PER_ACCELERATION_TICK;
step_rate += delta_rate; // Increment velocity
dt = (1000000*60)/step_rate; // Compute new time increment
if (dt < dt_min) {dt = dt_min;} // If target rate reached, cruise.
step_delay = dt-settings.pulse_microseconds;
}
}
}
}
}
#endif
void limits_go_home()
{
// Enable only the steppers, not the cycle. Cycle should be inactive/complete.
st_wake_up();
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate
// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
// circumvent the processes for executing motions in normal operation.
// NOTE: Only the abort runtime command can interrupt this process.
void limits_go_home(uint8_t cycle_mask)
{
if (sys.abort) { return; } // Block if system reset has been issued.
// Initialize homing in search mode to quickly engage the specified cycle_mask limit switches.
bool approach = true;
float homing_rate = settings.homing_seek_rate;
uint8_t invert_pin, idx;
uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1);
float target[N_AXIS];
// Search to engage all axes limit switches at faster homing seek rate.
homing_cycle(HOMING_SEARCH_CYCLE_0, true, false, settings.homing_seek_rate); // Search cycle 0
#ifdef HOMING_SEARCH_CYCLE_1
homing_cycle(HOMING_SEARCH_CYCLE_1, true, false, settings.homing_seek_rate); // Search cycle 1
#endif
#ifdef HOMING_SEARCH_CYCLE_2
homing_cycle(HOMING_SEARCH_CYCLE_2, true, false, settings.homing_seek_rate); // Search cycle 2
#endif
delay_ms(settings.homing_debounce_delay); // Delay to debounce signal
// Determine travel distance to the furthest homing switch based on user max travel settings.
// NOTE: settings.max_travel[] is stored as a negative value.
float max_travel = settings.max_travel[X_AXIS];
if (max_travel > settings.max_travel[Y_AXIS]) { max_travel = settings.max_travel[Y_AXIS]; }
if (max_travel > settings.max_travel[Z_AXIS]) { max_travel = settings.max_travel[Z_AXIS]; }
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.
do {
// Initialize invert_pin boolean based on approach and invert pin user setting.
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { invert_pin = approach; }
else { invert_pin = !approach; }
// Set target location and rate for active axes.
uint8_t n_active_axis = 0;
for (idx=0; idx<N_AXIS; idx++) {
if (bit_istrue(cycle_mask,bit(idx))) {
n_active_axis++;
if (!approach) { target[idx] = -max_travel; }
else { target[idx] = max_travel; }
} else {
target[idx] = 0.0;
}
}
if (bit_istrue(settings.homing_dir_mask,(1<<X_DIRECTION_BIT))) { target[X_AXIS] = -target[X_AXIS]; }
if (bit_istrue(settings.homing_dir_mask,(1<<Y_DIRECTION_BIT))) { target[Y_AXIS] = -target[Y_AXIS]; }
if (bit_istrue(settings.homing_dir_mask,(1<<Z_DIRECTION_BIT))) { target[Z_AXIS] = -target[Z_AXIS]; }
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
// Reset homing axis locks based on cycle mask.
uint8_t axislock = 0;
if (bit_istrue(cycle_mask,bit(X_AXIS))) { axislock |= (1<<X_STEP_BIT); }
if (bit_istrue(cycle_mask,bit(Y_AXIS))) { axislock |= (1<<Y_STEP_BIT); }
if (bit_istrue(cycle_mask,bit(Z_AXIS))) { axislock |= (1<<Z_STEP_BIT); }
sys.homing_axis_lock = axislock;
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
uint8_t limit_state;
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, homing_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion.
#else
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion.
#endif
st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do {
// Check limit state. Lock out cycle axes when they change.
limit_state = LIMIT_PIN;
if (invert_pin) { limit_state ^= LIMIT_MASK; }
if (axislock & (1<<X_STEP_BIT)) {
if (limit_state & (1<<X_LIMIT_BIT)) { axislock &= ~(1<<X_STEP_BIT); }
}
if (axislock & (1<<Y_STEP_BIT)) {
if (limit_state & (1<<Y_LIMIT_BIT)) { axislock &= ~(1<<Y_STEP_BIT); }
}
if (axislock & (1<<Z_STEP_BIT)) {
if (limit_state & (1<<Z_LIMIT_BIT)) { axislock &= ~(1<<Z_STEP_BIT); }
}
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);
// Now in proximity of all limits. Carefully leave and approach switches in multiple cycles
// to precisely hone in on the machine zero location. Moves at slower homing feed rate.
int8_t n_cycle = N_HOMING_LOCATE_CYCLE;
while (n_cycle--) {
// Leave all switches to release them. After cycles complete, this is machine zero.
homing_cycle(HOMING_LOCATE_CYCLE, false, true, settings.homing_feed_rate);
delay_ms(settings.homing_debounce_delay);
st_reset(); // Immediately force kill steppers and reset step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared.
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;
if (n_cycle > 0) {
// Re-approach all switches to re-engage them.
homing_cycle(HOMING_LOCATE_CYCLE, true, false, settings.homing_feed_rate);
delay_ms(settings.homing_debounce_delay);
} 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
// 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.
for (idx=0; idx<N_AXIS; idx++) {
// Set up pull off targets and machine positions for limit switches homed in the negative
// direction, rather than the traditional positive. Leave non-homed positions as zero and
// do not move them.
// NOTE: settings.max_travel[] is stored as a negative value.
if (cycle_mask & bit(idx)) {
if ( settings.homing_dir_mask & get_direction_mask(idx) ) {
target[idx] = settings.homing_pulloff+settings.max_travel[idx];
sys.position[idx] = lround(settings.max_travel[idx]*settings.steps_per_mm[idx]);
} else {
target[idx] = -settings.homing_pulloff;
sys.position[idx] = 0;
}
} else { // Non-active cycle axis. Set target to not move during pull-off.
target[idx] = (float)sys.position[idx]/settings.steps_per_mm[idx];
}
}
st_go_idle(); // Call main stepper shutdown routine.
plan_sync_position(); // Sync planner position to current machine position for pull-off move.
#ifdef USE_LINE_NUMBERS
plan_buffer_line(target, settings.homing_seek_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan motion.
#else
plan_buffer_line(target, settings.homing_seek_rate, false); // Bypass mc_line(). Directly plan motion.
#endif
// Initiate pull-off using main motion control routines.
// TODO : Clean up state routines so that this motion still shows homing state.
sys.state = STATE_QUEUED;
bit_true_atomic(sys.execute, EXEC_CYCLE_START);
protocol_execute_runtime();
protocol_buffer_synchronize(); // Complete pull-off motion.
// Set system state to homing before returning.
sys.state = STATE_HOMING;
}
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
// and the workspace volume is in all negative space.
// the workspace volume is in all negative space, and the system is in normal operation.
void limits_soft_check(float *target)
{
if ( target[X_AXIS] > 0 || target[X_AXIS] < -settings.max_travel[X_AXIS] ||
target[Y_AXIS] > 0 || target[Y_AXIS] < -settings.max_travel[Y_AXIS] ||
target[Z_AXIS] > 0 || target[Z_AXIS] < -settings.max_travel[Z_AXIS] ) {
// 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) {
st_feed_hold();
while (sys.state == STATE_HOLD) {
protocol_execute_runtime();
if (sys.abort) { return; }
}
}
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { // NOTE: max_travel is stored as negative
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
sys.execute |= EXEC_CRIT_EVENT; // Indicate soft limit critical event
protocol_execute_runtime(); // Execute to enter critical event loop and system abort
// 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;
}
}
}

View File

@ -2,8 +2,8 @@
limits.h - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2013-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2013 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
@ -22,11 +22,14 @@
#ifndef limits_h
#define limits_h
// Initialize the limits module
void limits_init();
// Perform the homing cycle
void limits_go_home();
void limits_disable();
// 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);

121
main.c
View File

@ -1,9 +1,9 @@
/*
main.c - An embedded CNC Controller with rs274/ngc (g-code) support
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -19,95 +19,78 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has
been integral throughout the development of the higher level details of Grbl, as well
as being a consistent sounding board for the future of accessible and free CNC. */
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "config.h"
#include "system.h"
#include "serial.h"
#include "settings.h"
#include "protocol.h"
#include "gcode.h"
#include "planner.h"
#include "nuts_bolts.h"
#include "stepper.h"
#include "spindle_control.h"
#include "coolant_control.h"
#include "motion_control.h"
#include "gcode.h"
#include "protocol.h"
#include "limits.h"
#include "probe.h"
#include "report.h"
#include "settings.h"
#include "serial.h"
// Declare system global variable structure
system_t sys;
int main(void)
{
// Initialize system
serial_init(); // Setup serial baud rate and interrupts
// Initialize system upon power-up.
serial_init(); // Setup serial baud rate and interrupts
settings_init(); // Load grbl settings from EEPROM
st_init(); // Setup stepper pins and interrupt timers
sei(); // Enable interrupts
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
sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position
sei(); // Enable interrupts
// Check for power-up and set system alarm if homing is enabled to force homing cycle
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
// startup scripts, but allows access to settings and internal commands. Only a homing
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
// NOTE: The startup script will run after successful completion of the homing cycle, but
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (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.
// Execute system reset upon a system abort, where the main program will return to this loop.
// Once here, it is safe to re-initialize the system. At startup, the system will automatically
// reset to finish the initialization process.
if (sys.abort) {
// Reset system.
serial_reset_read_buffer(); // Clear serial read buffer
plan_init(); // Clear block buffer and planner variables
gc_init(); // Set g-code parser to default state
protocol_init(); // Clear incoming line data and execute startup lines
spindle_init();
coolant_init();
limits_init();
st_reset(); // Clear stepper subsystem variables.
// 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();
probe_init();
plan_reset(); // Clear block buffer and planner variables
st_reset(); // Clear stepper subsystem variables.
// Sync cleared gcode and planner positions to current system position, which is only
// cleared upon startup, not a reset/abort.
sys_sync_current_position();
// Sync cleared gcode and planner positions to current system position.
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; }
// Check for power-up and set system alarm if homing is enabled to force homing cycle
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
// startup scripts, but allows access to settings and internal commands. Only a homing
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
// NOTE: The startup script will run after successful completion of the homing cycle, but
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
#endif
// Check for and report alarm state after a reset, error, or an initial power up.
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_LOCK);
} else {
// All systems go. Set system to ready and execute startup script.
sys.state = STATE_IDLE;
protocol_execute_startup();
}
}
protocol_execute_runtime();
protocol_process(); // ... process the serial protocol
// When the serial protocol returns, there are no more characters in the serial read buffer to
// be processed and executed. This indicates that individual commands are being issued or
// streaming is finished. In either case, auto-cycle start, if enabled, any queued moves.
mc_auto_cycle_start();
// 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; }
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();
}
return 0; /* never reached */
return 0; /* Never reached */
}

View File

@ -2,8 +2,8 @@
motion_control.c - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 Sungeun K. Jeon
Copyright (c) 2011 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
@ -20,21 +20,19 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/io.h>
#include <util/delay.h>
#include <math.h>
#include <stdlib.h>
#include "system.h"
#include "settings.h"
#include "config.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 "nuts_bolts.h"
#include "stepper.h"
#include "planner.h"
#include "limits.h"
#include "protocol.h"
#include "probe.h"
#include "report.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
@ -43,7 +41,11 @@
// 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
{
// If enabled, check for soft limit violations. Placed here all line motions are picked up
// from everywhere in Grbl.
@ -52,28 +54,35 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) { return; }
// TODO: Backlash compensation may be installed here. Only need direction info to track when
// to insert a backlash line motion(s) before the intended line motion. Requires its own
// 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
// backlash steps will need to be also tracked. Not sure what the best strategy is for this,
// i.e. keep the planner independent and do the computations in the status reporting, or let
// the planner handle the position corrections. The latter may get complicated.
// TODO: Backlash comp positioning values may need to be kept at a system level, i.e. tracking
// true position after a feed hold in the middle of a backlash move. The difficulty is in making
// sure that the stepper subsystem and planner are working in sync, and the status report
// position also takes this into account.
// 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.
// 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.
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_runtime(); // Check for any run-time commands
if (sys.abort) { return; } // Bail, if system abort.
if ( plan_check_full_buffer() ) { mc_auto_cycle_start(); } // Auto-cycle start when buffer is full.
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
else { break; }
} while (1);
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate);
#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; }
@ -87,12 +96,16 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
// 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.
void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise)
{
#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
{
float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
float linear_travel = target[axis_linear] - position[axis_linear];
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
float r_axis1 = -offset[axis_1];
float rt_axis0 = target[axis_0] - center_axis0;
@ -100,7 +113,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
if (isclockwise) { // Correct atan2 output per direction
if (gc_state.modal.motion == MOTION_MODE_CW_ARC) { // Correct atan2 output per direction
if (angular_travel >= 0) { angular_travel -= 2*M_PI; }
} else {
if (angular_travel <= 0) { angular_travel += 2*M_PI; }
@ -109,11 +122,9 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
// (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit
// is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
// Computes: mm_per_arc_segment = sqrt(4*arc_tolerance*(2*radius-arc_tolerance)),
// segments = millimeters_of_travel/mm_per_arc_segment
float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
uint16_t segments = floor(millimeters_of_travel/
sqrt(4*settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
// 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) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
@ -122,8 +133,8 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
if (invert_feed_rate) { feed_rate *= segments; }
float theta_per_segment = angular_travel/segments;
float linear_per_segment = linear_travel/segments;
float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
and phi is the angle of rotation. Solution approach by Jens Geisler.
r_T = [cos(phi) -sin(phi);
@ -148,21 +159,17 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
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 - theta_per_segment*theta_per_segment;
float sin_T = theta_per_segment*0.16666667*(cos_T + 4);
float cos_T = 2.0 - theta_per_segment*theta_per_segment;
float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0);
cos_T *= 0.5;
float arc_target[N_AXIS];
float sin_Ti;
float cos_Ti;
float r_axisi;
uint16_t i;
uint8_t count = 0;
// Initialize the linear axis
arc_target[axis_linear] = position[axis_linear];
for (i = 1; i<segments; i++) { // Increment (segments-1)
for (i = 1; i<segments; i++) { // Increment (segments-1).
if (count < N_ARC_CORRECTION) {
// Apply vector rotation matrix. ~40 usec
@ -170,7 +177,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
r_axis1 = r_axisi;
count++;
} else {
} else {
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. ~375 usec
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti = cos(i*theta_per_segment);
@ -181,26 +188,37 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
}
// Update arc_target location
arc_target[axis_0] = center_axis0 + r_axis0;
arc_target[axis_1] = center_axis1 + r_axis1;
arc_target[axis_linear] += linear_per_segment;
mc_line(arc_target, feed_rate, invert_feed_rate);
position[axis_0] = center_axis0 + r_axis0;
position[axis_1] = center_axis1 + r_axis1;
position[axis_linear] += linear_per_segment;
#ifdef USE_LINE_NUMBERS
mc_line(position, feed_rate, invert_feed_rate, line_number);
#else
mc_line(position, feed_rate, invert_feed_rate);
#endif
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys.abort) { return; }
}
}
// Ensure last segment arrives at target location.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate);
#endif
}
// Execute dwell in seconds.
void mc_dwell(float seconds)
{
if (sys.state == STATE_CHECK_MODE) { return; }
uint16_t i = floor(1000/DWELL_TIME_STEP*seconds);
plan_synchronize();
delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder
protocol_buffer_synchronize();
delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder.
while (i-- > 0) {
// NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds.
protocol_execute_runtime();
@ -213,69 +231,101 @@ void mc_dwell(float seconds)
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
// executing the homing cycle. This prevents incorrect buffered plans after homing.
void mc_go_home()
void mc_homing_cycle()
{
sys.state = STATE_HOMING; // Set system state variable
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration
limits_go_home(); // Perform homing routine.
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
#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.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
// The machine 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 machine zero appropriately.
// At the same time, 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 tripping when hard limits are enabled.
// TODO: Need to improve dir_mask[] to be more axes independent.
float pulloff_target[N_AXIS];
clear_vector_float(pulloff_target); // Zero pulloff target.
clear_vector_long(sys.position); // Zero current position for now.
uint8_t dir_mask[N_AXIS];
dir_mask[X_AXIS] = (1<<X_DIRECTION_BIT);
dir_mask[Y_AXIS] = (1<<Y_DIRECTION_BIT);
dir_mask[Z_AXIS] = (1<<Z_DIRECTION_BIT);
uint8_t i;
for (i=0; i<N_AXIS; i++) {
// Set up pull off targets and machine positions for limit switches homed in the negative
// direction, rather than the traditional positive. Leave non-homed positions as zero and
// do not move them.
if (HOMING_LOCATE_CYCLE & bit(i)) {
if (settings.homing_dir_mask & dir_mask[i]) {
pulloff_target[i] = settings.homing_pulloff-settings.max_travel[i];
sys.position[i] = -lround(settings.max_travel[i]*settings.steps_per_mm[i]);
} else {
pulloff_target[i] = -settings.homing_pulloff;
}
}
}
sys_sync_current_position();
sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed.
// Homing cycle complete! Setup system for normal operation.
// -------------------------------------------------------------------------------------
mc_line(pulloff_target, settings.homing_seek_rate, false);
st_cycle_start(); // Move it. Nothing should be in the buffer except this motion.
plan_synchronize(); // Make sure the motion completes.
// Gcode parser position was circumvented by the limits_go_home() routine, so sync position now.
gc_sync_position();
// The gcode parser position circumvented by the pull-off maneuver, so sync position vectors.
sys_sync_current_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
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; }
// Finished!
limits_init();
}
// Auto-cycle start is a user setting that automatically begins the cycle when a user enters
// a valid motion command either manually or by a streaming tool. This is intended as a beginners
// feature to help new users to understand g-code. It can be disabled. Otherwise, the normal
// 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 mc_auto_cycle_start() { if (sys.auto_start) { st_cycle_start(); } }
// 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
{
if (sys.state != STATE_CYCLE) protocol_auto_cycle_start();
protocol_buffer_synchronize(); // Finish all queued commands
if (sys.abort) { return; } // Return if system reset has been issued.
// Perform probing cycle. Planner buffer should be empty at this point.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number);
#else
mc_line(target, feed_rate, invert_feed_rate);
#endif
// NOTE: Parser error-checking ensures the probe isn't already closed/triggered.
sys.probe_state = PROBE_ACTIVE;
bit_true_atomic(sys.execute, EXEC_CYCLE_START);
do {
protocol_execute_runtime();
if (sys.abort) { return; } // Check for system abort
} while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED));
if (sys.probe_state == PROBE_ACTIVE) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); }
protocol_execute_runtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
//Prep the new target based on the position that the probe triggered
uint8_t i;
for(i=0; i<N_AXIS; ++i){
target[i] = (float)sys.probe_position[i]/settings.steps_per_mm[i];
}
protocol_execute_runtime();
st_reset(); // Immediately force kill steppers and reset step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position for pull-off move.
#ifdef USE_LINE_NUMBERS
mc_line(target, feed_rate, invert_feed_rate, line_number); // Bypass mc_line(). Directly plan homing motion.
#else
mc_line(target, feed_rate, invert_feed_rate); // Bypass mc_line(). Directly plan homing motion.
#endif
bit_true_atomic(sys.execute, EXEC_CYCLE_START);
protocol_buffer_synchronize(); // Complete pull-off motion.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
// Gcode parser position was circumvented by the this routine, so sync position now.
gc_sync_position();
// Output the probe position as message.
report_probe_parameters();
}
// Method to ready the system to reset by setting the runtime reset command and killing any
@ -287,7 +337,7 @@ void mc_reset()
{
// Only this function can set the system reset. Helps prevent multiple kill calls.
if (bit_isfalse(sys.execute, EXEC_RESET)) {
sys.execute |= EXEC_RESET;
bit_true_atomic(sys.execute, EXEC_RESET);
// Kill spindle and coolant.
spindle_stop();
@ -297,10 +347,9 @@ void mc_reset()
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off.
switch (sys.state) {
case STATE_CYCLE: case STATE_HOLD: case STATE_HOMING: // case STATE_JOG:
sys.execute |= EXEC_ALARM; // Execute alarm state.
st_go_idle(); // Execute alarm force kills steppers. Position likely lost.
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) {
bit_true_atomic(sys.execute, EXEC_ALARM); // Flag main program to execute alarm state.
st_go_idle(); // Force kill steppers. Position has likely been lost.
}
}
}

View File

@ -2,8 +2,8 @@
motion_control.h - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -22,31 +22,43 @@
#ifndef motion_control_h
#define motion_control_h
#include <avr/io.h>
#include "planner.h"
#define HOMING_CYCLE_LINE_NUMBER -1
// 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
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction.
void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise);
#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
// Dwell for a specific number of seconds
void mc_dwell(float seconds);
// Perform homing cycle to locate machine zero. Requires limit switches.
void mc_go_home();
void mc_homing_cycle();
// Perform tool length probe cycle. Requires probe switch.
#ifdef USE_LINE_NUMBERS
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number);
#else
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate);
#endif
// Performs system reset. If in motion state, kills all motion and sets system alarm.
void mc_reset();
// Executes the auto cycle feature, if enabled.
void mc_auto_cycle_start();
#endif

View File

@ -2,8 +2,8 @@
nuts_bolts.c - Shared functions
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,13 +19,11 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <util/delay.h>
#include "nuts_bolts.h"
#include "gcode.h"
#include "planner.h"
#include "system.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
extern float __floatunsisf (unsigned long);
// Extracts a floating point value from a string. The following code is based loosely on
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
@ -34,7 +32,7 @@ extern float __floatunsisf (unsigned long);
// Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
int read_float(char *line, uint8_t *char_counter, float *float_ptr)
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
{
char *ptr = line + *char_counter;
unsigned char c;
@ -79,7 +77,7 @@ int read_float(char *line, uint8_t *char_counter, float *float_ptr)
// Convert integer into floating point.
float fval;
fval = __floatunsisf(intval);
fval = (float)intval;
// Apply decimal. Should perform no more than two floating point multiplications for the
// expected range of E0 to E-4.
@ -140,9 +138,22 @@ void delay_us(uint32_t us)
}
}
// Syncs all internal position vectors to the current system position.
void sys_sync_current_position()
// Returns direction mask according to Grbl internal axis indexing.
uint8_t get_direction_mask(uint8_t axis_idx)
{
plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
uint8_t axis_mask = 0;
switch( axis_idx ) {
case X_AXIS: axis_mask = (1<<X_DIRECTION_BIT); break;
case Y_AXIS: axis_mask = (1<<Y_DIRECTION_BIT); break;
case Z_AXIS: axis_mask = (1<<Z_DIRECTION_BIT); break;
}
return(axis_mask);
}
float hypot_f(float x, float y)
{
return(sqrt(x*x + y*y));
}

View File

@ -2,8 +2,8 @@
nuts_bolts.h - Header file for shared definitions, variables, and functions
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -22,12 +22,6 @@
#ifndef nuts_bolts_h
#define nuts_bolts_h
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
#include "defaults.h"
#define false 0
#define true 1
@ -44,60 +38,24 @@
// 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 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))
// Bit field and masking macros
#define bit(n) (1 << n)
#define bit_true(x,mask) (x |= mask)
#define bit_false(x,mask) (x &= ~mask)
#define bit_toggle(x,mask) (x ^= mask)
#define bit_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_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)
// Define system executor bit map. Used internally by runtime protocol as runtime command flags,
// which notifies the main program to execute the specified runtime command asynchronously.
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the runtime protocol only needs to check for a non-zero value to
// know when there is a runtime command to execute.
#define 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 system state bit map. The state variable primarily tracks the individual functions
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
// critical events.
#define STATE_IDLE 0 // Must be zero.
#define STATE_INIT 1 // Initial power up state.
#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start.
#define STATE_CYCLE 3 // Cycle is running
#define STATE_HOLD 4 // Executing feed hold
#define STATE_HOMING 5 // Performing homing cycle
#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access.
#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only.
// #define STATE_JOG 8 // Jogging mode is unique like homing.
// Define global system variables
typedef struct {
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t state; // Tracks the current state of Grbl.
volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks.
int32_t position[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.
} system_t;
extern system_t sys;
// Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is
// a pointer to the result variable. Returns true when it succeeds
int read_float(char *line, uint8_t *char_counter, float *float_ptr);
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr);
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
void delay_ms(uint16_t ms);
@ -105,7 +63,8 @@ void delay_ms(uint16_t ms);
// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us().
void delay_us(uint32_t us);
// Syncs Grbl's gcode and planner position variables with the system position.
void sys_sync_current_position();
uint8_t get_direction_mask(uint8_t i);
float hypot_f(float x, float y);
#endif

679
planner.c
View File

@ -2,8 +2,8 @@
planner.c - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 Sungeun K. Jeon
Copyright (c) 2011 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
@ -22,23 +22,21 @@
/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */
#include <inttypes.h>
#include <stdlib.h>
#include "system.h"
#include "planner.h"
#include "nuts_bolts.h"
#include "protocol.h"
#include "stepper.h"
#include "settings.h"
#include "config.h"
#include "protocol.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 block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
static volatile uint8_t block_buffer_tail; // Index of the block to process now
static uint8_t next_buffer_head; // Index of the next buffer head
// static *block_t block_buffer_planned;
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
static uint8_t block_buffer_head; // Index of the next block to be pushed
static uint8_t next_buffer_head; // Index of the next buffer head
static uint8_t block_buffer_planned; // Index of the optimally planned block
// Define planner variables
typedef struct {
@ -47,14 +45,12 @@ typedef struct {
// 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 last_x, last_y, last_z; // Target position of previous path line segment
} planner_t;
static planner_t pl;
// Returns the index of the next block in the ring buffer
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
static uint8_t next_block_index(uint8_t block_index)
// 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)
{
block_index++;
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
@ -63,7 +59,7 @@ static uint8_t next_block_index(uint8_t block_index)
// Returns the index of the previous block in the ring buffer
static uint8_t 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--;
@ -71,292 +67,180 @@ static uint8_t prev_block_index(uint8_t block_index)
}
/* STEPPER VELOCITY PROFILE DEFINITION
less than nominal rate-> +
+--------+ <- nominal_rate /|\
/ \ / | \
initial_rate -> + \ / | + <- next->initial_rate
| + <- next->initial_rate / | |
+-------------+ initial_rate -> +----+--+
time --> ^ ^ ^ ^
| | | |
decelerate distance decelerate distance
Calculates trapezoid parameters for stepper algorithm. Each block velocity profiles can be
described as either a trapezoidal or a triangular shape. The trapezoid occurs when the block
reaches the nominal speed of the block and cruises for a period of time. A triangle occurs
when the nominal speed is not reached within the block. Some other special cases exist,
such as pure ac/de-celeration velocity profiles from beginning to end or a trapezoid that
has no deceleration period when the next block resumes acceleration.
The following function determines the type of velocity profile and stores the minimum required
information for the stepper algorithm to execute the calculated profiles. In this case, only
the new initial rate and n_steps until deceleration are computed, since the stepper algorithm
already handles acceleration and cruising and just needs to know when to start decelerating.
*/
static void calculate_trapezoid_for_block(block_t *block, float entry_speed_sqr, float exit_speed_sqr)
{
// Compute new initial rate for stepper algorithm
block->initial_rate = ceil(sqrt(entry_speed_sqr)*(RANADE_MULTIPLIER/(60*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
// TODO: Compute new nominal rate if a feedrate override occurs.
// block->nominal_rate = ceil(feed_rate*(RANADE_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
// Compute efficiency variable for following calculations. Removes a float divide and multiply.
// TODO: If memory allows, this can be kept in the block buffer since it doesn't change, even after feed holds.
float steps_per_mm_div_2_acc = block->step_event_count/(2*block->acceleration*block->millimeters);
// First determine intersection distance (in steps) from the exit point for a triangular profile.
// Computes: steps_intersect = steps/mm * ( distance/2 + (v_entry^2-v_exit^2)/(4*acceleration) )
int32_t intersect_distance = ceil( 0.5*(block->step_event_count + steps_per_mm_div_2_acc*(entry_speed_sqr-exit_speed_sqr)) );
// Check if this is a pure acceleration block by a intersection distance less than zero. Also
// prevents signed and unsigned integer conversion errors.
if (intersect_distance <= 0) {
block->decelerate_after = 0;
} else {
// Determine deceleration distance (in steps) from nominal speed to exit speed for a trapezoidal profile.
// Value is never negative. Nominal speed is always greater than or equal to the exit speed.
// Computes: steps_decelerate = steps/mm * ( (v_nominal^2 - v_exit^2)/(2*acceleration) )
block->decelerate_after = ceil(steps_per_mm_div_2_acc * (block->nominal_speed_sqr - exit_speed_sqr));
// The lesser of the two triangle and trapezoid distances always defines the velocity profile.
if (block->decelerate_after > intersect_distance) { block->decelerate_after = intersect_distance; }
// Finally, check if this is a pure deceleration block.
if (block->decelerate_after > block->step_event_count) { block->decelerate_after = block->step_event_count; }
}
}
/* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed
/ \
current->entry_speed -> + \
| + <- next->entry_speed
| + <- next->entry_speed (aka exit speed)
+-------------+
time -->
Recalculates the motion plan according to the following algorithm:
Recalculates the motion plan according to the following basic guidelines:
1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_speed)
so that:
a. The junction speed is equal to or less than the maximum junction speed limit
b. No speed reduction within one block requires faster deceleration than the acceleration limits.
c. The last (or newest appended) block is planned from a complete stop.
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
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
a. The speed increase within one block would require faster acceleration than the acceleration limits.
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, all blocks have a junction entry speed that will allow all speed changes
to be performed using the overall limiting acceleration value, and where no junction speed is greater
than the max limit. In other words, it just computed the fastest possible velocity profile through all
buffered blocks, where the final buffered block is planned to come to a full stop when the buffer is fully
executed. Finally it will:
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
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
guidelines for a new optimal plan.
3. Convert the plan to data that the stepper algorithm needs. Only block trapezoids adjacent to a
a planner-modified junction speed with be updated, the others are assumed ok as is.
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
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
recomputed as stated in the general guidelines.
All planner computations(1)(2) are performed in floating point to minimize numerical round-off errors. Only
when planned values are converted to stepper rate parameters(3), these are integers. If another motion block
is added while executing, the planner will re-plan and update the stored optimal velocity profile as it goes.
Planner buffer index mapping:
- 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
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
this requirement when encountered by the plan_discard_current_block() routine during a cycle.
Conceptually, the planner works like blowing up a balloon, where the balloon is the velocity profile. It's
constrained by the speeds at the beginning and end of the buffer, along with the maximum junction speeds and
nominal speeds of each block. Once a plan is computed, or balloon filled, this is the optimal velocity profile
through all of the motions in the buffer. Whenever a new block is added, this changes some of the limiting
conditions, or how the balloon is filled, so it has to be re-calculated to get the new optimal velocity profile.
Also, since the planner only computes on what's in the planner buffer, some motions with lots of short line
segments, like arcs, 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 decelerate to a stop at the end of the
buffer. There are a few simple solutions to this: (1) Maximize the machine acceleration. The planner will be
able to compute higher speed profiles within the same combined distance. (2) Increase line segment(s) distance.
The more combined distance the planner has to use, the faster it can go. (3) Increase the MINIMUM_PLANNER_SPEED.
Not recommended. This will change what speed the planner plans to at the end of the buffer. Can lead to lost
steps when coming to a stop. (4) [BEST] Increase the planner buffer size. The more combined distance, the
bigger the balloon, or faster it can go. But this is not possible for 328p Arduinos because its limited memory
is already maxed out. Future ARM versions should not have this issue, with look-ahead planner blocks numbering
up to a hundred or more.
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
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
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
to compute an optimal plan, so select carefully. The Arduino 328p memory is already maxed out, but future
ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
NOTE: Since this function is constantly re-calculating for every new incoming block, it must be as efficient
as possible. For example, in situations like arc generation or complex curves, the short, rapid line segments
can execute faster than new blocks can be added, and the planner buffer will then starve and empty, leading
to weird hiccup-like jerky motions.
*/
static void planner_recalculate()
{
// float entry_speed_sqr;
// uint8_t block_index = block_buffer_head;
// block_t *previous = NULL;
// block_t *current = NULL;
// block_t *next;
// while (block_index != block_buffer_tail) {
// block_index = prev_block_index( block_index );
// next = current;
// current = previous;
// previous = &block_buffer[block_index];
//
// if (next && current) {
// if (next != block_buffer_planned) {
// if (previous == block_buffer_tail) { block_buffer_planned = next; }
// else {
//
// if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
// current->recalculate_flag = true; // Almost always changes. So force recalculate.
// entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
// if (entry_speed_sqr < current->max_entry_speed_sqr) {
// current->entry_speed_sqr = entry_speed_sqr;
// } else {
// current->entry_speed_sqr = current->max_entry_speed_sqr;
// }
// } else {
// block_buffer_planned = current;
// }
// }
// } else {
// break;
// }
// }
// }
//
// block_index = block_buffer_planned;
// next = &block_buffer[block_index];
// current = prev_block_index(block_index);
// while (block_index != block_buffer_head) {
//
// // If the current block is an acceleration block, but it is not long enough to complete the
// // full speed change within the block, we need to adjust the exit speed accordingly. Entry
// // speeds have already been reset, maximized, and reverse planned by reverse planner.
// if (current->entry_speed_sqr < next->entry_speed_sqr) {
// // Compute block exit speed based on the current block speed and distance
// // Computes: v_exit^2 = v_entry^2 + 2*acceleration*distance
// entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
//
// // If it's less than the stored value, update the exit speed and set recalculate flag.
// if (entry_speed_sqr < next->entry_speed_sqr) {
// next->entry_speed_sqr = entry_speed_sqr;
// next->recalculate_flag = true;
// }
// }
//
// // Recalculate if current block entry or exit junction speed has changed.
// if (current->recalculate_flag || next->recalculate_flag) {
// // NOTE: Entry and exit factors always > 0 by all previous logic operations.
// calculate_trapezoid_for_block(current, current->entry_speed_sqr, next->entry_speed_sqr);
// current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
// }
//
// current = next;
// next = &block_buffer[block_index];
// block_index = next_block_index( block_index );
// }
//
// // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
// calculate_trapezoid_for_block(next, next->entry_speed_sqr, MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED);
// next->recalculate_flag = false;
// TODO: No over-write protection exists for the executing block. For most cases this has proven to be ok, but
// for feed-rate overrides, something like this is essential. Place a request here to the stepper driver to
// find out where in the planner buffer is the a safe place to begin re-planning from.
// if (block_buffer_head != block_buffer_tail) {
{
// 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.
float entry_speed_sqr;
plan_block_t *next;
plan_block_t *current = &block_buffer[block_index];
// Perform reverse planner pass. Skip the head(end) block since it is already initialized, and skip the
// tail(first) block to prevent over-writing of the initial entry speed.
uint8_t block_index = prev_block_index( block_buffer_head ); // Assume buffer is not empty.
block_t *current = &block_buffer[block_index]; // Head block-1 = Newly appended block
block_t *next;
if (block_index != block_buffer_tail) { block_index = prev_block_index( block_index ); }
while (block_index != block_buffer_tail) {
next = current;
current = &block_buffer[block_index];
// TODO: Determine maximum entry speed at junction for feedrate overrides, since they can alter
// the planner nominal speeds at any time. This calc could be done in the override handler, but
// this could require an additional variable to be stored to differentiate the programmed nominal
// speeds, max junction speed, and override speeds/scalar.
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
// 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) {
next = current;
current = &block_buffer[block_index];
block_index = plan_prev_block_index(block_index);
current->entry_speed_sqr = current->max_entry_speed_sqr;
current->recalculate_flag = true; // Almost always changes. So force recalculate.
// 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 (next->entry_speed_sqr < current->max_entry_speed_sqr) {
// Computes: v_entry^2 = v_exit^2 + 2*acceleration*distance
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
if (entry_speed_sqr < current->max_entry_speed_sqr) {
current->entry_speed_sqr = entry_speed_sqr;
} else {
current->entry_speed_sqr = current->max_entry_speed_sqr;
}
}
}
block_index = prev_block_index( block_index );
}
}
}
}
// Perform forward planner pass. Begins junction speed adjustments after tail(first) block.
// Also recalculate trapezoids, block by block, as the forward pass completes the plan.
block_index = next_block_index(block_buffer_tail);
next = &block_buffer[block_buffer_tail]; // Places tail(first) block into current
// 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);
while (block_index != block_buffer_head) {
current = next;
next = &block_buffer[block_index];
// If the current block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the exit speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
if (current->entry_speed_sqr < next->entry_speed_sqr) {
// Compute block exit speed based on the current block speed and distance
// Computes: v_exit^2 = v_entry^2 + 2*acceleration*distance
entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
// If it's less than the stored value, update the exit speed and set recalculate flag.
if (entry_speed_sqr < next->entry_speed_sqr) {
next->entry_speed_sqr = entry_speed_sqr;
next->recalculate_flag = true;
}
// 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.
if (current->entry_speed_sqr < next->entry_speed_sqr) {
entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
// If true, current block is full-acceleration and we can move the planned pointer forward.
if (entry_speed_sqr < next->entry_speed_sqr) {
next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
block_buffer_planned = block_index; // Set optimal plan pointer.
}
// Recalculate if current block entry or exit junction speed has changed.
if (current->recalculate_flag || next->recalculate_flag) {
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
calculate_trapezoid_for_block(current, current->entry_speed_sqr, next->entry_speed_sqr);
current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
}
block_index = next_block_index( block_index );
}
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
calculate_trapezoid_for_block(next, next->entry_speed_sqr, MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED);
next->recalculate_flag = false;
// }
}
// 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_init()
void plan_reset()
{
block_buffer_tail = block_buffer_head;
next_buffer_head = next_block_index(block_buffer_head);
// block_buffer_planned = block_buffer_head;
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)
block_buffer_planned = 0; // = block_buffer_tail;
}
inline void plan_discard_current_block()
void plan_discard_current_block()
{
if (block_buffer_head != block_buffer_tail) {
block_buffer_tail = next_block_index( block_buffer_tail );
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
uint8_t block_index = plan_next_block_index( block_buffer_tail );
// Push block_buffer_planned pointer, if encountered.
if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
block_buffer_tail = block_index;
}
}
inline block_t *plan_get_current_block()
plan_block_t *plan_get_current_block()
{
if (block_buffer_head == block_buffer_tail) { return(NULL); }
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
return(&block_buffer[block_buffer_tail]);
}
float plan_get_exec_block_exit_speed()
{
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 ) );
}
// Returns the availability status of the block ring buffer. True, if full.
uint8_t plan_check_full_buffer()
{
@ -364,185 +248,170 @@ uint8_t plan_check_full_buffer()
return(false);
}
// 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 plan_synchronize()
/* 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
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
{
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
}
}
// Prepare and initialize new block
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
#ifdef USE_LINE_NUMBERS
block->line_number = line_number;
#endif
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
// millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
// All position data passed to the planner must be in terms of machine position to keep the planner
// independent of any coordinate system changes and offsets, which are handled by the g-code parser.
// NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control.
// 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).
void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate)
{
// Prepare to set up new block
block_t *block = &block_buffer[block_buffer_head];
// Calculate target position in absolute steps
int32_t target[N_AXIS];
target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]);
target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]);
target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]);
// 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];
float unit_vec[N_AXIS], delta_mm;
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
// Calculate target position in absolute steps. This conversion should be consistent throughout.
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
// Number of steps for each axis
block->steps_x = labs(target[X_AXIS]-pl.position[X_AXIS]);
block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]);
block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]);
block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z));
// Bail if this is a zero-length block
if (block->step_event_count == 0) { return; };
// Number of steps for each axis and determine max step events
block->steps[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.
// 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.
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0 ) { block->direction_bits |= get_direction_mask(idx); }
// Incrementally compute total move distance by Euclidean norm. First add square of each term.
block->millimeters += delta_mm*delta_mm;
}
block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt()
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return; }
// Compute path vector in terms of absolute step target and current positions
float delta_mm[N_AXIS];
delta_mm[X_AXIS] = x-pl.last_x;
delta_mm[Y_AXIS] = y-pl.last_y;
delta_mm[Z_AXIS] = z-pl.last_z;
block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] +
delta_mm[Z_AXIS]*delta_mm[Z_AXIS]);
// 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; }
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration limited
// by the maximum possible values. Block rapids rates are computed or feed rates are scaled down so
// they don't exceed the maximum axes velocities. The block acceleration is maximized based on direction
// and axes properties as well.
// 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.
uint8_t i;
float unit_vec[N_AXIS], inverse_unit_vec_value;
float inverse_unit_vec_value;
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides
block->acceleration = SOME_LARGE_VALUE; // Scaled down to maximum acceleration in loop
for (i=0; i<N_AXIS; i++) {
if (delta_mm[i] == 0) {
unit_vec[i] = 0; // Store zero value. And avoid divide by zero.
} else {
// Compute unit vector and its absolute inverse value
unit_vec[i] = delta_mm[i]*inverse_millimeters;
inverse_unit_vec_value = abs(1.0/unit_vec[i]);
// Check and limit feed rate against max axis velocities and scale accelerations to maximums
feed_rate = min(feed_rate,settings.max_velocity[i]*inverse_unit_vec_value);
block->acceleration = min(block->acceleration,settings.acceleration[i]*inverse_unit_vec_value);
float junction_cos_theta = 0;
for (idx=0; idx<N_AXIS; idx++) {
if (unit_vec[idx] != 0) { // Avoid divide by zero.
unit_vec[idx] *= inverse_millimeters; // Complete unit vector calculation
inverse_unit_vec_value = fabs(1.0/unit_vec[idx]); // Inverse to remove multiple float divides.
// Check and limit feed rate against max individual axis velocities and accelerations
feed_rate = min(feed_rate,settings.max_rate[idx]*inverse_unit_vec_value);
block->acceleration = min(block->acceleration,settings.acceleration[idx]*inverse_unit_vec_value);
// Incrementally compute cosine of angle between previous and current path. Cos(theta) of the junction
// between the current move and the previous move is simply the dot product of the two unit vectors,
// where prev_unit_vec is negative. Used later to compute maximum junction speed.
junction_cos_theta -= pl.previous_unit_vec[idx] * unit_vec[idx];
}
}
// Compute nominal speed and rates
block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min)^2. Always > 0
block->nominal_rate = ceil(feed_rate*(RANADE_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
// Compute the acceleration and distance traveled per step event for the stepper algorithm.
block->rate_delta = ceil(block->acceleration*
((RANADE_MULTIPLIER/(60.0*60.0))/(ISR_TICKS_PER_SECOND*ACCELERATION_TICKS_PER_SECOND))); // (mult*mm/isr_tic/accel_tic)
block->d_next = ceil((block->millimeters*RANADE_MULTIPLIER)/block->step_event_count); // (mult*mm/step)
// Compute direction bits. Bit enabled always means direction is negative.
block->direction_bits = 0;
if (unit_vec[X_AXIS] < 0) { block->direction_bits |= (1<<X_DIRECTION_BIT); }
if (unit_vec[Y_AXIS] < 0) { block->direction_bits |= (1<<Y_DIRECTION_BIT); }
if (unit_vec[Z_AXIS] < 0) { block->direction_bits |= (1<<Z_DIRECTION_BIT); }
// TODO: Need to check this method handling zero junction speeds when starting from rest.
if (block_buffer_head == block_buffer_tail) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
} else {
/*
Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
Let a circle be tangent to both previous and current path line segments, where the junction
deviation is defined as the distance from the junction to the closest edge of the circle,
colinear with the circle center. The circular segment joining the two paths represents the
path of centripetal acceleration. Solve for max velocity based on max acceleration about the
radius of the circle, defined indirectly by junction deviation. This may be also viewed as
path width or max_jerk in the previous grbl version. This approach does not actually deviate
from path, but used as a robust way to compute cornering speeds, as it takes into account the
nonlinearities of both the junction angle and junction velocity.
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
// deviation is defined as the distance from the junction to the closest edge of the circle,
// colinear with the circle center. The circular segment joining the two paths represents the
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
// path width or max_jerk in the previous grbl version. This approach does not actually deviate
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
// nonlinearities of both the junction angle and junction velocity.
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
// is exactly the same. Instead of motioning all the way to junction point, the machine will
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
// a continuous mode path, but ARM-based microcontrollers most certainly do.
NOTE: 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.
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
block->max_entry_speed_sqr = MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED;
if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed_sqr > 0.0)) {
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
float cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
- pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS]
- pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ;
// Skip and use default max junction speed for 0 degree acute junction.
if (cos_theta < 0.95) {
block->max_entry_speed_sqr = min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr);
// Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds.
if (cos_theta > -0.95) {
// Compute maximum junction velocity based on maximum acceleration and junction deviation
float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
block->max_entry_speed_sqr = min(block->max_entry_speed_sqr,
block->acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2));
}
}
}
// Initialize block entry speed. Compute block entry velocity backwards from user-defined MINIMUM_PLANNER_SPEED.
// TODO: This could be moved to the planner recalculate function.
block->entry_speed_sqr = min( block->max_entry_speed_sqr,
MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED + 2*block->acceleration*block->millimeters);
// Set new block to be recalculated for conversion to stepper data.
block->recalculate_flag = true;
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the
// two junctions. However, this shouldn't be a significant problem except in extreme circumstances.
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
(block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
}
// 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;
// Update planner position
memcpy(pl.position, target, sizeof(target)); // pl.position[] = target[]
pl.last_x = x;
pl.last_y = y;
pl.last_z = z;
memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
// Update buffer head and next buffer head indices
// New block is all set. Update buffer head and next buffer head indices.
block_buffer_head = next_buffer_head;
next_buffer_head = next_block_index(block_buffer_head);
planner_recalculate();
next_buffer_head = plan_next_block_index(block_buffer_head);
// Finish up by recalculating the plan with the new block.
planner_recalculate();
}
// Reset the planner position vectors. Called by the system abort/initialization routine.
void plan_set_current_position(int32_t x, int32_t y, int32_t z)
void plan_sync_position()
{
pl.position[X_AXIS] = x;
pl.position[Y_AXIS] = y;
pl.position[Z_AXIS] = z;
pl.last_x = x/settings.steps_per_mm[X_AXIS];
pl.last_y = y/settings.steps_per_mm[Y_AXIS];
pl.last_z = z/settings.steps_per_mm[Z_AXIS];
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
pl.position[idx] = sys.position[idx];
}
}
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
void plan_cycle_reinitialize(int32_t step_events_remaining)
void plan_cycle_reinitialize()
{
block_t *block = &block_buffer[block_buffer_tail]; // Point to partially completed block
// Only remaining millimeters and step_event_count need to be updated for planner recalculate.
// Other variables (step_x, step_y, step_z, rate_delta, etc.) all need to remain the same to
// ensure the original planned motion is resumed exactly.
block->millimeters = (block->millimeters*step_events_remaining)/block->step_event_count;
block->step_event_count = step_events_remaining;
// Re-plan from a complete stop. Reset planner entry speeds and flags.
block->entry_speed_sqr = 0.0;
block->max_entry_speed_sqr = 0.0;
block->recalculate_flag = true;
// 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();
}

View File

@ -2,8 +2,8 @@
planner.h - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -21,62 +21,75 @@
#ifndef planner_h
#define planner_h
#include "system.h"
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
#define BLOCK_BUFFER_SIZE 18
#ifdef USE_LINE_NUMBERS
#define BLOCK_BUFFER_SIZE 16
#else
#define BLOCK_BUFFER_SIZE 18
#endif
#endif
// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in
// the source g-code and may never actually be reached if acceleration management is active.
// 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.
typedef struct {
// Fields used by the bresenham algorithm for tracing the line
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
uint32_t steps_x, steps_y, steps_z; // Step count along each axis
int32_t step_event_count; // The number of step events required to complete this block
// 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 nominal_speed_sqr; // The nominal speed for this block in mm/min
float entry_speed_sqr; // Entry speed at previous-current block junction in mm/min
float max_entry_speed_sqr; // Maximum allowable junction entry speed in mm/min
float millimeters; // The total travel of this block in mm
float acceleration;
uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction
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
#ifdef USE_LINE_NUMBERS
int32_t line_number;
#endif
} plan_block_t;
// Settings for the trapezoid generator
uint32_t initial_rate; // The step rate at start of block
int32_t rate_delta; // The steps/minute to add or subtract when changing speed (must be positive)
uint32_t decelerate_after; // The index of the step event on which to start decelerating
uint32_t nominal_rate; // The nominal step rate for this block in step_events/minute
uint32_t d_next; // Scaled distance to next step
} block_t;
// Initialize the motion plan subsystem
void plan_init();
// Initialize and reset the motion plan subsystem
void plan_reset();
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
// millimaters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
// 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.
void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate);
#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
// 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 current block. Returns NULL if buffer empty
block_t *plan_get_current_block();
plan_block_t *plan_get_current_block();
// Called periodically by step segment buffer. Mostly used internally by planner.
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();
// Reset the planner position vector (in steps)
void plan_set_current_position(int32_t x, int32_t y, int32_t z);
void plan_sync_position();
// Reinitialize plan with a partially completed block
void plan_cycle_reinitialize(int32_t step_events_remaining);
void plan_cycle_reinitialize();
// Returns the status of the block ring buffer. True, if buffer is full.
uint8_t plan_check_full_buffer();
// Block until all buffered steps are executed
void plan_synchronize();
#endif

79
print.c
View File

@ -2,8 +2,8 @@
print.c - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,12 +22,11 @@
/* This code was initially inspired by the wiring_serial module by David A. Mellis which
used to be a part of the Arduino project. */
#include <avr/pgmspace.h>
#include "config.h"
#include "system.h"
#include "serial.h"
#include "settings.h"
void printString(const char *s)
{
while (*s)
@ -77,32 +76,52 @@ void print_uint8_base2(uint8_t n)
serial_write('0' + buf[i - 1]);
}
static void print_uint32_base10(unsigned long n)
void print_uint8_base10(uint8_t n)
{
unsigned char buf[10];
uint8_t i = 0;
if (n == 0) {
serial_write('0');
return;
}
unsigned char buf[3];
uint8_t i = 0;
while (n > 0) {
buf[i++] = n % 10 + '0';
n /= 10;
}
for (; i > 0; i--)
serial_write(buf[i - 1]);
}
void print_uint32_base10(unsigned long n)
{
if (n == 0) {
serial_write('0');
return;
}
unsigned char buf[10];
uint8_t i = 0;
while (n > 0) {
buf[i++] = n % 10 + '0';
buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
serial_write(buf[i-1]);
serial_write('0' + buf[i-1]);
}
void printInteger(long n)
{
if (n < 0) {
serial_write('-');
n = -n;
print_uint32_base10((-n));
} else {
print_uint32_base10(n);
}
print_uint32_base10(n);
}
// Convert float to string by immediately converting to a long integer, which contains
@ -110,14 +129,14 @@ void printInteger(long n)
// may be set by the user. The integer is then efficiently converted to a string.
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// techniques are actually just slightly slower. Found this out the hard way.
void printFloat(float n)
void printFloat(float n, uint8_t decimal_places)
{
if (n < 0) {
serial_write('-');
n = -n;
}
uint8_t decimals = settings.decimal_places;
uint8_t decimals = decimal_places;
while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4.
n *= 100;
decimals -= 2;
@ -129,16 +148,16 @@ void printFloat(float n)
unsigned char buf[10];
uint8_t i = 0;
uint32_t a = (long)n;
buf[settings.decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
while(a > 0) {
if (i == settings.decimal_places) { i++; } // Skip decimal point location
if (i == decimal_places) { i++; } // Skip decimal point location
buf[i++] = (a % 10) + '0'; // Get digit
a /= 10;
}
while (i < settings.decimal_places) {
while (i < decimal_places) {
buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1)
}
if (i == settings.decimal_places) { // Fill in leading zero, if needed.
if (i == decimal_places) { // Fill in leading zero, if needed.
i++;
buf[i++] = '0';
}
@ -147,3 +166,27 @@ void printFloat(float n)
for (; i > 0; i--)
serial_write(buf[i-1]);
}
// Floating value printing handlers for special variables types used in Grbl and are defined
// 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)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH);
} else {
printFloat(n,N_DECIMAL_COORDVALUE_MM);
}
}
void printFloat_RateValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH);
} else {
printFloat(n,N_DECIMAL_RATEVALUE_MM);
}
}
void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); }

18
print.h
View File

@ -2,8 +2,8 @@
print.h - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -31,8 +31,22 @@ void printPgmString(const char *s);
void printInteger(long n);
void print_uint32_base10(uint32_t n);
void print_uint8_base2(uint8_t n);
void printFloat(float n);
void print_uint8_base10(uint8_t n);
void printFloat(float n, uint8_t decimal_places);
// 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);
#endif

51
probe.c Normal file
View File

@ -0,0 +1,51 @@
/*
probe.c - code pertaining to probing methods
Part of Grbl
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 <http://www.gnu.org/licenses/>.
*/
#include "system.h"
#include "probe.h"
// Probe pin initialization routine.
void probe_init()
{
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
}
// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state()
{
return(!(PROBE_PIN & PROBE_MASK));
}
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
// 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);
}
}
}

39
probe.h Normal file
View File

@ -0,0 +1,39 @@
/*
probe.h - code pertaining to probing methods
Part of Grbl
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 <http://www.gnu.org/licenses/>.
*/
#ifndef probe_h
#define probe_h
// Values that define the probing state machine.
#define PROBE_OFF 0 // No probing. (Must be zero.)
#define PROBE_ACTIVE 1 // Actively watching the input pin.
// Probe pin initialization routine.
void probe_init();
// Returns probe pin state.
uint8_t probe_get_state();
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
void probe_state_monitor();
#endif

View File

@ -1,9 +1,9 @@
/*
protocol.c - the serial protocol master control unit
protocol.c - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -19,70 +19,151 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "system.h"
#include "serial.h"
#include "settings.h"
#include "protocol.h"
#include "gcode.h"
#include "serial.h"
#include "print.h"
#include "settings.h"
#include "config.h"
#include "nuts_bolts.h"
#include "planner.h"
#include "stepper.h"
#include "report.h"
#include "motion_control.h"
#include "report.h"
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static uint8_t char_counter; // Last character counter in line variable.
static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters.
void protocol_init()
{
char_counter = 0; // Reset line input
iscomment = false;
report_init_message(); // Welcome message
PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins
PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation.
PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt
}
// 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
// Executes user startup script, if stored.
void protocol_execute_startup()
{
uint8_t n;
for (n=0; n < N_STARTUP_LINE; n++) {
if (!(settings_read_startup_line(n, line))) {
report_status_message(STATUS_SETTING_READ_FAIL);
} else {
if (line[0] != 0) {
printString(line); // Echo startup line to indicate execution.
report_status_message(gc_execute_line(line));
}
}
}
}
if (line[0] == 0) {
// Empty or comment line. Send status message for syncing purposes.
report_status_message(STATUS_OK);
// 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))) {
sys.execute |= EXEC_FEED_HOLD;
} else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) {
sys.execute |= EXEC_CYCLE_START;
}
} else if (line[0] == '$') {
// Grbl '$' system command
report_status_message(system_execute_line(line));
} else if (sys.state == STATE_ALARM) {
// Everything else is gcode. Block if in alarm mode.
report_status_message(STATUS_ALARM_LOCK);
} else {
// Parse and execute g-code block!
report_status_message(gc_execute_line(line));
}
}
/*
GRBL PRIMARY LOOP:
*/
void protocol_main_loop()
{
// ------------------------------------------------------------
// Complete initialization procedures upon a power-up or reset.
// ------------------------------------------------------------
// Print welcome message
report_init_message();
// Check for and report alarm state after a reset, error, or an initial power up.
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_LOCK);
} else {
// All systems go!
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;
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
line[char_counter] = 0; // Set string termination character.
protocol_execute_line(line); // Line is complete. Execute it!
iscomment = false;
char_counter = 0;
} else {
if (iscomment) {
// Throw away all comment characters
if (c == ')') {
// End of comment. Resume line.
iscomment = false;
}
} else {
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 == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
// 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
// } 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
// 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;
} 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
// completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start();
protocol_execute_runtime(); // 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.
@ -96,8 +177,8 @@ ISR(PINOUT_INT_vect)
// limit switches, or the main program.
void protocol_execute_runtime()
{
if (sys.execute) { // Enter only if any bit flag is true
uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times
uint8_t rt_exec = sys.execute; // Copy to avoid calling volatile multiple times
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
@ -105,11 +186,13 @@ void protocol_execute_runtime()
if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) {
sys.state = STATE_ALARM; // Set system alarm state
// Critical event. Only hard/soft limit errors currently qualify.
// 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) {
report_alarm_message(ALARM_LIMIT_ERROR);
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(sys.execute,EXEC_RESET); // Disable any existing reset
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
@ -125,7 +208,7 @@ void protocol_execute_runtime()
// to indicate the possible severity of the problem.
report_alarm_message(ALARM_ABORT_CYCLE);
}
bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT));
bit_false_atomic(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT));
}
// Execute system abort.
@ -137,200 +220,80 @@ void protocol_execute_runtime()
// Execute and serial print status
if (rt_exec & EXEC_STATUS_REPORT) {
report_realtime_status();
bit_false(sys.execute,EXEC_STATUS_REPORT);
bit_false_atomic(sys.execute,EXEC_STATUS_REPORT);
}
// Initiate stepper feed hold
// Execute a feed hold with deceleration, only during cycle.
if (rt_exec & EXEC_FEED_HOLD) {
st_feed_hold(); // Initiate feed hold.
bit_false(sys.execute,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.
}
bit_false_atomic(sys.execute,EXEC_FEED_HOLD);
}
// 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.
}
}
bit_false_atomic(sys.execute,EXEC_CYCLE_START);
}
// Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer.
// 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) {
st_cycle_reinitialize();
bit_false(sys.execute,EXEC_CYCLE_STOP);
}
if (rt_exec & EXEC_CYCLE_START) {
st_cycle_start(); // Issue cycle start command to stepper subsystem
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) {
sys.auto_start = true; // Re-enable auto start after feed hold.
}
bit_false(sys.execute,EXEC_CYCLE_START);
if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; }
else { sys.state = STATE_IDLE; }
bit_false_atomic(sys.execute,EXEC_CYCLE_STOP);
}
}
// 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.
// Reload step segment buffer
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { st_prep_buffer(); }
}
// Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// settings, initiating the homing cycle, and toggling switch states. This differs from
// the runtime command module by being susceptible to when Grbl is ready to execute the
// next line during a cycle, so for switches like block delete, the switch only effects
// the lines that are processed afterward, not necessarily real-time during a cycle,
// since there are motions already stored in the buffer. However, this 'lag' should not
// be an issue, since these commands are not typically used during a cycle.
uint8_t protocol_execute_line(char *line)
{
// Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help
if(line[0] == '$') {
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_UNSUPPORTED_STATEMENT); }
else { report_grbl_settings(); }
break;
case '#' : // Print gcode parameters
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
else { report_gcode_parameters(); }
break;
case 'G' : // Prints gcode parser state
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
else { report_gcode_modes(); }
break;
case 'C' : // Set check g-code mode
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
// Perform reset when toggling off. Check g-code mode should only work if Grbl
// is idle and ready, regardless of alarm locks. This is mainly to keep things
// simple and consistent.
if ( sys.state == STATE_CHECK_MODE ) {
mc_reset();
report_feedback_message(MESSAGE_DISABLED);
} else {
if (sys.state) { return(STATUS_IDLE_ERROR); }
sys.state = STATE_CHECK_MODE;
report_feedback_message(MESSAGE_ENABLED);
}
break;
case 'X' : // Disable alarm lock
if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents.
}
break;
case 'H' : // Perform homing cycle
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Only perform homing if Grbl is idle or lost.
if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) {
mc_go_home();
if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing.
} else { return(STATUS_IDLE_ERROR); }
} else { return(STATUS_SETTING_DISABLED); }
break;
// case 'J' : break; // Jogging methods
// TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
// susceptible to other runtime commands except for e-stop. The jogging function is intended to
// be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
// steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
// start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
// motion by repeatedly toggling to slow the motion to the desired location. Location data would
// need to be updated real-time and supplied to the user through status queries.
// More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the
// block buffer without having the planner plan them. It would need to manage de/ac-celerations
// on its own carefully. This approach could be effective and possibly size/memory efficient.
case 'N' : // Startup lines.
if ( line[++char_counter] == 0 ) { // Print startup lines
for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
if (!(settings_read_startup_line(helper_var, line))) {
report_status_message(STATUS_SETTING_READ_FAIL);
} else {
report_startup_line(helper_var,line);
}
}
break;
} else { // Store startup line
helper_var = true; // Set helper_var to flag storing method.
// No break. Continues into default: to read remaining command characters.
}
default : // Storing setting methods
if(!read_float(line, &char_counter, &parameter)) { return(STATUS_BAD_NUMBER_FORMAT); }
if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_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_UNSUPPORTED_STATEMENT); }
return(settings_store_global_setting(parameter, value));
}
}
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
} else {
return(gc_execute_line(line)); // Everything else is gcode
}
}
// Process and report status one line of incoming serial data. Performs an initial filtering
// by removing spaces and comments and capitalizing all letters.
void protocol_process()
// 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()
{
uint8_t c;
while((c = serial_read()) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached
// Runtime command check point before executing line. Prevent any furthur line executions.
// NOTE: If there is no line, this function should quickly return to the main program when
// the buffer empties of non-executable data.
protocol_execute_runtime();
if (sys.abort) { return; } // Bail to main program upon system abort
if (char_counter > 0) {// Line is complete. Then execute!
line[char_counter] = 0; // Terminate string
report_status_message(protocol_execute_line(line));
} else {
// Empty or comment line. Skip block.
report_status_message(STATUS_OK); // Send status message for syncing purposes.
}
char_counter = 0; // Reset line buffer index
iscomment = false; // Reset comment flag
} else {
if (iscomment) {
// Throw away all comment characters
if (c == ')') {
// End of comment. Resume line.
iscomment = false;
}
} else {
if (c <= ' ') {
// Throw away whitepace and control characters
} else if (c == '/') {
// Block delete not supported. Ignore character.
} else if (c == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
iscomment = true;
} else if (char_counter >= LINE_BUFFER_SIZE-1) {
// Throw away any characters beyond the end of the line buffer
} else if (c >= 'a' && c <= 'z') { // Upcase lowercase
line[char_counter++] = c-'a'+'A';
} else {
line[char_counter++] = c;
}
}
}
}
// 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
}
}
// 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); } }

View File

@ -1,9 +1,9 @@
/*
protocol.h - the serial protocol master control unit
protocol.h - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,32 +21,36 @@
#ifndef protocol_h
#define protocol_h
#include <avr/sleep.h>
// Line buffer size from the serial input stream to be executed.
// NOTE: Not a problem except for extreme cases, but the line buffer size can be too small
// and g-code blocks can get truncated. Officially, the g-code standards support up to 256
// characters. In future versions, this will be increased, when we know how much extra
// memory space we can invest into here or we re-write the g-code parser not to have his
// 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 50
#define LINE_BUFFER_SIZE 70
#endif
// Initialize the serial protocol
void protocol_init();
// Read command lines from the serial port and execute them as they
// come in. Blocks until the serial buffer is emptied.
void protocol_process();
// Executes one line of input according to protocol
uint8_t protocol_execute_line(char *line);
// Starts Grbl main loop. It handles all incoming characters from the serial port and executes
// 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();
// Execute the startup script lines stored in EEPROM upon initialization
void protocol_execute_startup();
// Notify the stepper subsystem to start executing the g-code program in buffer.
// void protocol_cycle_start();
// Reinitializes the buffer after a feed hold for a resume.
// void protocol_cycle_reinitialize();
// Initiates a feed hold of the running program
// void protocol_feed_hold();
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start();
// Block until all buffered steps are executed
void protocol_buffer_synchronize();
#endif

237
report.c
View File

@ -2,7 +2,7 @@
report.c - reporting and messaging methods
Part of Grbl
Copyright (c) 2012-2013 Sungeun K. Jeon
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
@ -26,13 +26,15 @@
methods to accomodate their needs.
*/
#include <avr/pgmspace.h>
#include "system.h"
#include "report.h"
#include "print.h"
#include "settings.h"
#include "nuts_bolts.h"
#include "gcode.h"
#include "coolant_control.h"
#include "planner.h"
#include "spindle_control.h"
#include "stepper.h"
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
@ -50,32 +52,40 @@ void report_status_message(uint8_t status_code)
} else {
printPgmString(PSTR("error: "));
switch(status_code) {
case STATUS_BAD_NUMBER_FORMAT:
printPgmString(PSTR("Bad number format")); break;
case STATUS_EXPECTED_COMMAND_LETTER:
printPgmString(PSTR("Expected command letter")); break;
case STATUS_UNSUPPORTED_STATEMENT:
printPgmString(PSTR("Unsupported statement")); break;
case STATUS_ARC_RADIUS_ERROR:
printPgmString(PSTR("Invalid radius")); break;
case STATUS_MODAL_GROUP_VIOLATION:
printPgmString(PSTR("Modal group violation")); 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_VALUE_NEG:
printPgmString(PSTR("Value < 0.0")); 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("Busy or queued")); break;
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"));
}
@ -90,8 +100,10 @@ void report_alarm_message(int8_t alarm_code)
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(". MPos?\r\n"));
printPgmString(PSTR("\r\n"));
delay_ms(500); // Force delay to ensure message clears serial write buffer.
}
@ -146,44 +158,64 @@ void report_grbl_help() {
// Grbl global settings print out.
// NOTE: The numbering scheme here must correlate to storing in settings.c
void report_grbl_settings() {
printPgmString(PSTR("$0=")); printFloat(settings.steps_per_mm[X_AXIS]);
printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat(settings.steps_per_mm[Y_AXIS]);
printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat(settings.steps_per_mm[Z_AXIS]);
printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printFloat(settings.max_velocity[X_AXIS]);
printPgmString(PSTR(" (x v_max, mm/min)\r\n$4=")); printFloat(settings.max_velocity[Y_AXIS]);
printPgmString(PSTR(" (y v_max, mm/min)\r\n$5=")); printFloat(settings.max_velocity[Z_AXIS]);
printPgmString(PSTR(" (z v_max, mm/min)\r\n$6=")); printFloat(settings.acceleration[X_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (x accel, mm/sec^2)\r\n$7=")); printFloat(settings.acceleration[Y_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (y accel, mm/sec^2)\r\n$8=")); printFloat(settings.acceleration[Z_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat(settings.max_travel[X_AXIS]);
printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat(settings.max_travel[Y_AXIS]);
printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat(settings.max_travel[Z_AXIS]);
printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds);
printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate);
printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.invert_mask);
printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.invert_mask);
printPgmString(PSTR(")\r\n$15=")); printInteger(settings.stepper_idle_lock_time);
printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat(settings.arc_tolerance);
printPgmString(PSTR(" (arc tolerance, mm)\r\n$18=")); printInteger(settings.decimal_places);
printPgmString(PSTR(" (n-decimals, int)\r\n$19=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
printPgmString(PSTR(" (report inches, bool)\r\n$20=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START));
printPgmString(PSTR(" (auto start, bool)\r\n$21=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); printInteger(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (soft limits, bool)\r\n$23=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$24=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$25=")); printInteger(settings.homing_dir_mask);
printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask);
printPgmString(PSTR(")\r\n$26=")); printFloat(settings.homing_feed_rate);
printPgmString(PSTR(" (homing feed, mm/min)\r\n$27=")); printFloat(settings.homing_seek_rate);
printPgmString(PSTR(" (homing seek, mm/min)\r\n$28=")); printInteger(settings.homing_debounce_delay);
printPgmString(PSTR(" (homing debounce, msec)\r\n$29=")); printFloat(settings.homing_pulloff);
printPgmString(PSTR("$0=")); printFloat_SettingValue(settings.steps_per_mm[X_AXIS]);
printPgmString(PSTR(" (x, step/mm)\r\n$1=")); printFloat_SettingValue(settings.steps_per_mm[Y_AXIS]);
printPgmString(PSTR(" (y, step/mm)\r\n$2=")); printFloat_SettingValue(settings.steps_per_mm[Z_AXIS]);
printPgmString(PSTR(" (z, step/mm)\r\n$3=")); printFloat_SettingValue(settings.max_rate[X_AXIS]);
printPgmString(PSTR(" (x max rate, mm/min)\r\n$4=")); printFloat_SettingValue(settings.max_rate[Y_AXIS]);
printPgmString(PSTR(" (y max rate, mm/min)\r\n$5=")); printFloat_SettingValue(settings.max_rate[Z_AXIS]);
printPgmString(PSTR(" (z max rate, mm/min)\r\n$6=")); printFloat_SettingValue(settings.acceleration[X_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (x accel, mm/sec^2)\r\n$7=")); printFloat_SettingValue(settings.acceleration[Y_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (y accel, mm/sec^2)\r\n$8=")); printFloat_SettingValue(settings.acceleration[Z_AXIS]/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat_SettingValue(-settings.max_travel[X_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat_SettingValue(-settings.max_travel[Y_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat_SettingValue(-settings.max_travel[Z_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); print_uint8_base10(settings.pulse_microseconds);
printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
printPgmString(PSTR(")\r\n$14=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask);
printPgmString(PSTR(")\r\n$15=")); print_uint8_base10(settings.stepper_idle_lock_time);
printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat_SettingValue(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat_SettingValue(settings.arc_tolerance);
printPgmString(PSTR(" (arc tolerance, mm)\r\n$19=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
printPgmString(PSTR(" (auto start, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
printPgmString(PSTR(" (invert limit pins, bool)\r\n$23=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (soft limits, bool)\r\n$24=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$25=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$26=")); print_uint8_base10(settings.homing_dir_mask);
printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
printPgmString(PSTR(")\r\n$27=")); printFloat_SettingValue(settings.homing_feed_rate);
printPgmString(PSTR(" (homing feed, mm/min)\r\n$28=")); printFloat_SettingValue(settings.homing_seek_rate);
printPgmString(PSTR(" (homing seek, mm/min)\r\n$29=")); print_uint8_base10(settings.homing_debounce_delay);
printPgmString(PSTR(" (homing debounce, msec)\r\n$30=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
}
// Prints gcode coordinate offset parameters
void report_gcode_parameters()
// Prints current probe parameters. Upon a probe command, these parameters are updated upon a
// successful probe or upon a failed probe with the G38.3 without errors command (if supported).
// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed.
void report_probe_parameters()
{
uint8_t i;
float print_position[N_AXIS];
// Report in terms of machine position.
printPgmString(PSTR("[PRB:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] = sys.probe_position[i]/settings.steps_per_mm[i];
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
}
printPgmString(PSTR("]\r\n"));
}
// Prints Grbl NGC parameters (coordinate offsets, probing)
void report_ngc_parameters()
{
float coord_data[N_AXIS];
uint8_t coord_select, i;
@ -194,74 +226,72 @@ void report_gcode_parameters()
}
printPgmString(PSTR("[G"));
switch (coord_select) {
case 0: printPgmString(PSTR("54:")); break;
case 1: printPgmString(PSTR("55:")); break;
case 2: printPgmString(PSTR("56:")); break;
case 3: printPgmString(PSTR("57:")); break;
case 4: printPgmString(PSTR("58:")); break;
case 5: printPgmString(PSTR("59:")); break;
case 6: printPgmString(PSTR("28:")); break;
case 7: printPgmString(PSTR("30:")); break;
// case 8: printPgmString(PSTR("92:")); break; // G92.2, G92.3 not supported. Hence not stored.
}
case 6: printPgmString(PSTR("28")); break;
case 7: printPgmString(PSTR("30")); break;
default: print_uint8_base10(coord_select+54); break; // G54-G59
}
printPgmString(PSTR(":"));
for (i=0; i<N_AXIS; i++) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(coord_data[i]*INCH_PER_MM); }
else { printFloat(coord_data[i]); }
printFloat_CoordValue(coord_data[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
else { printPgmString(PSTR("]\r\n")); }
}
}
printPgmString(PSTR("[G92:")); // Print G92,G92.1 which are not persistent in memory
for (i=0; i<N_AXIS; i++) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(gc.coord_offset[i]*INCH_PER_MM); }
else { printFloat(gc.coord_offset[i]); }
printFloat_CoordValue(gc_state.coord_offset[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
else { printPgmString(PSTR("]\r\n")); }
}
printPgmString(PSTR("[TLO:")); // Print tool length offset value
printFloat_CoordValue(gc_state.tool_length_offset);
printPgmString(PSTR("]\r\n"));
report_probe_parameters(); // Print probe parameters. Not persistent in memory.
}
// Print current gcode parser mode state
void report_gcode_modes()
{
switch (gc.motion_mode) {
switch (gc_state.modal.motion) {
case MOTION_MODE_SEEK : printPgmString(PSTR("[G0")); break;
case MOTION_MODE_LINEAR : printPgmString(PSTR("[G1")); break;
case MOTION_MODE_CW_ARC : printPgmString(PSTR("[G2")); break;
case MOTION_MODE_CCW_ARC : printPgmString(PSTR("[G3")); break;
case MOTION_MODE_CANCEL : printPgmString(PSTR("[G80")); break;
case MOTION_MODE_NONE : printPgmString(PSTR("[G80")); break;
}
printPgmString(PSTR(" G"));
printInteger(gc.coord_select+54);
print_uint8_base10(gc_state.modal.coord_select+54);
if (gc.plane_axis_0 == X_AXIS) {
if (gc.plane_axis_1 == Y_AXIS) { printPgmString(PSTR(" G17")); }
else { printPgmString(PSTR(" G18")); }
} else { printPgmString(PSTR(" G19")); }
switch (gc_state.modal.plane_select) {
case PLANE_SELECT_XY : printPgmString(PSTR(" G17")); break;
case PLANE_SELECT_ZX : printPgmString(PSTR(" G18")); break;
case PLANE_SELECT_YZ : printPgmString(PSTR(" G19")); break;
}
if (gc.inches_mode) { printPgmString(PSTR(" G20")); }
else { printPgmString(PSTR(" G21")); }
if (gc_state.modal.units == UNITS_MODE_MM) { printPgmString(PSTR(" G21")); }
else { printPgmString(PSTR(" G20")); }
if (gc.absolute_mode) { printPgmString(PSTR(" G90")); }
if (gc_state.modal.distance == DISTANCE_MODE_ABSOLUTE) { printPgmString(PSTR(" G90")); }
else { printPgmString(PSTR(" G91")); }
if (gc.inverse_feed_rate_mode) { printPgmString(PSTR(" G93")); }
if (gc_state.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { printPgmString(PSTR(" G93")); }
else { printPgmString(PSTR(" G94")); }
switch (gc.program_flow) {
switch (gc_state.modal.program_flow) {
case PROGRAM_FLOW_RUNNING : printPgmString(PSTR(" M0")); break;
case PROGRAM_FLOW_PAUSED : printPgmString(PSTR(" M1")); break;
case PROGRAM_FLOW_COMPLETED : printPgmString(PSTR(" M2")); break;
}
switch (gc.spindle_direction) {
case 1 : printPgmString(PSTR(" M3")); break;
case -1 : printPgmString(PSTR(" M4")); break;
case 0 : printPgmString(PSTR(" M5")); break;
switch (gc_state.modal.spindle) {
case SPINDLE_ENABLE_CW : printPgmString(PSTR(" M3")); break;
case SPINDLE_ENABLE_CCW : printPgmString(PSTR(" M4")); break;
case SPINDLE_DISABLE : printPgmString(PSTR(" M5")); break;
}
switch (gc.coolant_mode) {
switch (gc_state.modal.coolant) {
case COOLANT_DISABLE : printPgmString(PSTR(" M9")); break;
case COOLANT_FLOOD_ENABLE : printPgmString(PSTR(" M8")); break;
#ifdef ENABLE_M7
@ -270,11 +300,10 @@ void report_gcode_modes()
}
printPgmString(PSTR(" T"));
printInteger(gc.tool);
print_uint8_base10(gc_state.tool);
printPgmString(PSTR(" F"));
if (gc.inches_mode) { printFloat(gc.feed_rate*INCH_PER_MM); }
else { printFloat(gc.feed_rate); }
printFloat_RateValue(gc_state.feed_rate);
printPgmString(PSTR("]\r\n"));
}
@ -282,11 +311,21 @@ void report_gcode_modes()
// Prints specified startup line
void report_startup_line(uint8_t n, char *line)
{
printPgmString(PSTR("$N")); printInteger(n);
printPgmString(PSTR("$N")); print_uint8_base10(n);
printPgmString(PSTR("=")); printString(line);
printPgmString(PSTR("\r\n"));
}
// Prints build info line
void report_build_info(char *line)
{
printPgmString(PSTR("[" GRBL_VERSION "." GRBL_VERSION_BUILD ":"));
printString(line);
printPgmString(PSTR("]\r\n"));
}
// 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
@ -306,7 +345,6 @@ void report_realtime_status()
// Report current machine state
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
// case STATE_INIT: printPgmString(PSTR("<Init")); break; // Never observed
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
@ -319,22 +357,35 @@ void report_realtime_status()
printPgmString(PSTR(",MPos:"));
for (i=0; i< N_AXIS; i++) {
print_position[i] = current_position[i]/settings.steps_per_mm[i];
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; }
printFloat(print_position[i]);
printFloat_CoordValue(print_position[i]);
printPgmString(PSTR(","));
}
// Report work position
printPgmString(PSTR("WPos:"));
for (i=0; i< N_AXIS; i++) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM;
} else {
print_position[i] -= gc.coord_system[i]+gc.coord_offset[i];
}
printFloat(print_position[i]);
print_position[i] -= gc_state.coord_system[i]+gc_state.coord_offset[i];
if (i == TOOL_LENGTH_OFFSET_AXIS) { print_position[i] -= gc_state.tool_length_offset; }
printFloat_CoordValue(print_position[i]);
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
}
#ifdef USE_LINE_NUMBERS
// Report current line number
printPgmString(PSTR(",Ln:"));
int32_t ln=0;
plan_block_t * pb = plan_get_current_block();
if(pb != NULL) {
ln = pb->line_number;
}
printInteger(ln);
#endif
#ifdef REPORT_REALTIME_RATE
// Report realtime rate
printPgmString(PSTR(",F:"));
printFloat_RateValue(st_get_realtime_rate());
#endif
printPgmString(PSTR(">\r\n"));
}

View File

@ -2,7 +2,7 @@
report.h - reporting and messaging methods
Part of Grbl
Copyright (c) 2012-2013 Sungeun K. Jeon
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
@ -20,26 +20,44 @@
#ifndef report_h
#define report_h
// Define Grbl status codes.
#define STATUS_OK 0
#define STATUS_BAD_NUMBER_FORMAT 1
#define STATUS_EXPECTED_COMMAND_LETTER 2
#define STATUS_UNSUPPORTED_STATEMENT 3
#define STATUS_ARC_RADIUS_ERROR 4
#define STATUS_MODAL_GROUP_VIOLATION 5
#define STATUS_INVALID_STATEMENT 6
#define STATUS_SETTING_DISABLED 7
#define STATUS_SETTING_VALUE_NEG 8
#define STATUS_SETTING_STEP_PULSE_MIN 9
#define STATUS_SETTING_READ_FAIL 10
#define STATUS_IDLE_ERROR 11
#define STATUS_ALARM_LOCK 12
#define STATUS_SOFT_LIMIT_ERROR 13
#define STATUS_EXPECTED_COMMAND_LETTER 1
#define STATUS_BAD_NUMBER_FORMAT 2
#define STATUS_INVALID_STATEMENT 3
#define STATUS_NEGATIVE_VALUE 4
#define STATUS_SETTING_DISABLED 5
#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_SOFT_LIMIT_ERROR 10
#define STATUS_OVERFLOW 11
#define STATUS_GCODE_UNSUPPORTED_COMMAND 20
#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21
#define STATUS_GCODE_UNDEFINED_FEED_RATE 22
#define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23
#define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24
#define STATUS_GCODE_WORD_REPEATED 25
#define STATUS_GCODE_NO_AXIS_WORDS 26
#define STATUS_GCODE_INVALID_LINE_NUMBER 27
#define STATUS_GCODE_VALUE_WORD_MISSING 28
#define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29
#define STATUS_GCODE_G53_INVALID_MOTION_MODE 30
#define STATUS_GCODE_AXIS_WORDS_EXIST 31
#define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32
#define STATUS_GCODE_INVALID_TARGET 33
#define STATUS_GCODE_ARC_RADIUS_ERROR 34
#define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35
#define STATUS_GCODE_PROBE_TRIGGERED 36
#define STATUS_GCODE_UNUSED_WORDS 37
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 38
// 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 MESSAGE_CRITICAL_EVENT 1
@ -69,8 +87,11 @@ void report_grbl_settings();
// Prints realtime status report
void report_realtime_status();
// Prints Grbl persistent coordinate parameters
void report_gcode_parameters();
// Prints recorded probe position
void report_probe_parameters();
// Prints Grbl NGC parameters (coordinate offsets, probe)
void report_ngc_parameters();
// Prints current g-code parser mode state
void report_gcode_modes();
@ -78,4 +99,7 @@ void report_gcode_modes();
// Prints startup line
void report_startup_line(uint8_t n, char *line);
// Prints build info and user info
void report_build_info(char *line);
#endif

View File

@ -2,8 +2,8 @@
serial.c - Low level functions for sending and recieving bytes via the serial port
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -23,19 +23,21 @@
used to be a part of the Arduino project. */
#include <avr/interrupt.h>
#include "system.h"
#include "serial.h"
#include "config.h"
#include "motion_control.h"
#include "protocol.h"
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_buffer_head = 0;
uint8_t rx_buffer_tail = 0;
volatile uint8_t rx_buffer_tail = 0;
uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_buffer_head = 0;
volatile uint8_t tx_buffer_tail = 0;
#ifdef ENABLE_XONXOFF
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
@ -49,6 +51,7 @@ volatile uint8_t tx_buffer_tail = 0;
}
#endif
void serial_init()
{
// Set baud rate
@ -72,6 +75,7 @@ void serial_init()
// defaults to 8-bit, no parity, 1 stop bit
}
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = tx_buffer_head + 1;
@ -90,15 +94,11 @@ void serial_write(uint8_t data) {
UCSR0B |= (1 << UDRIE0);
}
// Data Register Empty Interrupt handler
#ifdef __AVR_ATmega644P__
ISR(USART0_UDRE_vect)
#else
ISR(USART_UDRE_vect)
#endif
ISR(SERIAL_UDRE)
{
// Temporary tx_buffer_tail (to optimize for volatile)
uint8_t tail = tx_buffer_tail;
uint8_t tail = tx_buffer_tail; // Temporary tx_buffer_tail (to optimize for volatile)
#ifdef ENABLE_XONXOFF
if (flow_ctrl == SEND_XOFF) {
@ -124,14 +124,18 @@ ISR(USART_UDRE_vect)
if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}
uint8_t serial_read()
{
if (rx_buffer_head == rx_buffer_tail) {
uint8_t tail = rx_buffer_tail; // Temporary rx_buffer_tail (to optimize for volatile)
if (rx_buffer_head == tail) {
return SERIAL_NO_DATA;
} else {
uint8_t data = rx_buffer[rx_buffer_tail];
rx_buffer_tail++;
if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; }
uint8_t data = rx_buffer[tail];
tail++;
if (tail == RX_BUFFER_SIZE) { tail = 0; }
rx_buffer_tail = tail;
#ifdef ENABLE_XONXOFF
if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
@ -144,11 +148,8 @@ uint8_t serial_read()
}
}
#ifdef __AVR_ATmega644P__
ISR(USART0_RX_vect)
#else
ISR(USART_RX_vect)
#endif
ISR(SERIAL_RX)
{
uint8_t data = UDR0;
uint8_t next_head;
@ -156,9 +157,9 @@ ISR(USART_RX_vect)
// Pick off runtime command characters directly from the serial stream. These characters are
// not passed into the buffer, but these set system state flag bits for runtime execution.
switch (data) {
case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true
case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true
case CMD_STATUS_REPORT: bit_true_atomic(sys.execute, EXEC_STATUS_REPORT); break; // Set as true
case CMD_CYCLE_START: bit_true_atomic(sys.execute, EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: bit_true_atomic(sys.execute, EXEC_FEED_HOLD); break; // Set as true
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
default: // Write character to buffer
next_head = rx_buffer_head + 1;
@ -177,9 +178,11 @@ ISR(USART_RX_vect)
#endif
}
//TODO: else alarm on overflow?
}
}
void serial_reset_read_buffer()
{
rx_buffer_tail = rx_buffer_head;

View File

@ -2,8 +2,8 @@
serial.c - Low level functions for sending and recieving bytes via the serial port
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,7 +25,6 @@
#ifndef serial_h
#define serial_h
#include "nuts_bolts.h"
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128

View File

@ -2,8 +2,8 @@
settings.c - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -19,29 +19,15 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/io.h>
#include "protocol.h"
#include "report.h"
#include "stepper.h"
#include "nuts_bolts.h"
#include "system.h"
#include "settings.h"
#include "eeprom.h"
#include "protocol.h"
#include "report.h"
#include "limits.h"
settings_t settings;
// Version 4 outdated settings record
typedef struct {
float steps_per_mm[N_AXIS];
uint8_t microsteps;
uint8_t pulse_microseconds;
float default_feed_rate;
uint8_t invert_mask;
float mm_per_arc_segment;
float acceleration;
float junction_deviation;
} settings_v4_t;
// Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char *line)
@ -50,6 +36,14 @@ void settings_store_startup_line(uint8_t n, char *line)
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
}
// Method to store build info into EEPROM
void settings_store_build_info(char *line)
{
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)
{
@ -57,6 +51,7 @@ void settings_write_coord_data(uint8_t coord_select, float *coord_data)
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()
{
@ -64,53 +59,51 @@ void write_global_settings()
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
}
// Method to reset Grbl global settings back to defaults.
void settings_reset(bool reset_all) {
// Reset all settings or only the migration settings to the new version.
if (reset_all) {
settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM;
settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM;
settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM;
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
settings.default_feed_rate = DEFAULT_FEEDRATE;
settings.max_velocity[X_AXIS] = DEFAULT_RAPID_FEEDRATE;
settings.max_velocity[Y_AXIS] = DEFAULT_RAPID_FEEDRATE;
settings.max_velocity[Z_AXIS] = DEFAULT_RAPID_FEEDRATE;
settings.acceleration[X_AXIS] = DEFAULT_ACCELERATION;
settings.acceleration[Y_AXIS] = DEFAULT_ACCELERATION;
settings.acceleration[Z_AXIS] = DEFAULT_ACCELERATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
}
// New settings since last version
void settings_reset() {
settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM;
settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM;
settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM;
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
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.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE;
settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE;
settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME;
settings.decimal_places = DEFAULT_DECIMAL_PLACES;
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[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL);
settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL);
settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL);
write_global_settings();
}
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_startup_line(uint8_t n, char *line)
{
uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) {
// Reset line with default value
line[0] = 0;
line[0] = 0; // Empty line
settings_store_startup_line(n, line);
return(false);
} else {
@ -118,6 +111,21 @@ uint8_t settings_read_startup_line(uint8_t n, char *line)
}
}
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_build_info(char *line)
{
if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) {
// Reset line with default value
line[0] = 0; // Empty line
settings_store_build_info(line);
return(false);
} else {
return(true);
}
}
// Read selected coordinate data from EEPROM. Updates pointed coord_data value.
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
{
@ -132,26 +140,18 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
}
}
// Reads Grbl global settings struct from EEPROM.
uint8_t read_global_settings() {
// Check version-byte of eeprom
uint8_t version = eeprom_get_char(0);
if (version == SETTINGS_VERSION) {
// Read settings-record and check checksum
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) {
return(false);
}
} else {
if (version <= 4) {
// Migrate from settings version 4 to current version.
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) {
return(false);
}
settings_reset(false); // Old settings ok. Write new settings only.
} else {
return(false);
}
return(false);
}
return(true);
}
@ -159,28 +159,27 @@ uint8_t read_global_settings() {
// A helper method to set settings from command line
uint8_t settings_store_global_setting(int parameter, float value) {
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
switch(parameter) {
case 0: case 1: case 2:
if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); }
settings.steps_per_mm[parameter] = value; break;
case 3: settings.max_velocity[X_AXIS] = value; break;
case 4: settings.max_velocity[Y_AXIS] = value; break;
case 5: settings.max_velocity[Z_AXIS] = value; break;
case 3: settings.max_rate[X_AXIS] = value; break;
case 4: settings.max_rate[Y_AXIS] = value; break;
case 5: settings.max_rate[Z_AXIS] = value; break;
case 6: settings.acceleration[X_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 7: settings.acceleration[Y_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 8: settings.acceleration[Z_AXIS] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 9: settings.max_travel[X_AXIS] = value; break;
case 10: settings.max_travel[Y_AXIS] = value; break;
case 11: settings.max_travel[Z_AXIS] = value; break;
case 9: settings.max_travel[X_AXIS] = -value; break; // Store as negative for grbl internal use.
case 10: settings.max_travel[Y_AXIS] = -value; break; // Store as negative for grbl internal use.
case 11: settings.max_travel[Z_AXIS] = -value; break; // Store as negative for grbl internal use.
case 12:
if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
settings.pulse_microseconds = round(value); break;
case 13: settings.default_feed_rate = value; break;
case 14: settings.invert_mask = trunc(value); break;
case 13: settings.step_invert_mask = trunc(value); break;
case 14: settings.dir_invert_mask = trunc(value); break;
case 15: settings.stepper_idle_lock_time = round(value); break;
case 16: settings.junction_deviation = fabs(value); break;
case 17: settings.arc_tolerance = value; break;
case 18: settings.decimal_places = round(value); break;
case 19:
if (value) { settings.flags |= BITFLAG_REPORT_INCHES; }
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
@ -193,26 +192,33 @@ uint8_t settings_store_global_setting(int parameter, float value) {
if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; }
break;
case 22:
case 22: // Reset to ensure change. Immediate re-init may cause problems.
if (value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
break;
case 23:
if (value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
break;
case 23:
case 24:
if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; }
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
break;
case 24:
case 25:
if (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;
case 25: settings.homing_dir_mask = trunc(value); break;
case 26: settings.homing_feed_rate = value; break;
case 27: settings.homing_seek_rate = value; break;
case 28: settings.homing_debounce_delay = round(value); break;
case 29: settings.homing_pulloff = value; break;
case 26: settings.homing_dir_mask = trunc(value); break;
case 27: settings.homing_feed_rate = value; break;
case 28: settings.homing_seek_rate = value; break;
case 29: settings.homing_debounce_delay = round(value); break;
case 30: settings.homing_pulloff = value; break;
default:
return(STATUS_INVALID_STATEMENT);
}
@ -220,11 +226,12 @@ uint8_t settings_store_global_setting(int parameter, float value) {
return(STATUS_OK);
}
// Initialize the config subsystem
void settings_init() {
if(!read_global_settings()) {
report_status_message(STATUS_SETTING_READ_FAIL);
settings_reset(true);
settings_reset();
report_grbl_settings();
}
// Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing.

View File

@ -2,8 +2,8 @@
settings.h - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011-2013 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
@ -22,14 +22,15 @@
#ifndef settings_h
#define settings_h
#include <math.h>
#include "nuts_bolts.h"
#include "system.h"
#define GRBL_VERSION "0.9a"
#define GRBL_VERSION "0.9f"
#define GRBL_VERSION_BUILD "20140706"
// 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 53
#define SETTINGS_VERSION 8
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
@ -38,6 +39,7 @@
#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 EEPROM memory address location values for Grbl settings and parameters
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
@ -46,6 +48,7 @@
#define EEPROM_ADDR_GLOBAL 1
#define EEPROM_ADDR_PARAMETERS 512
#define EEPROM_ADDR_STARTUP_BLOCK 768
#define EEPROM_ADDR_BUILD_INFO 992
// Define EEPROM address indexing for coordinate parameters
#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1)
@ -58,24 +61,21 @@
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
typedef struct {
float steps_per_mm[N_AXIS];
uint8_t microsteps;
uint8_t pulse_microseconds;
float default_feed_rate;
float default_seek_rate;
uint8_t invert_mask;
float arc_tolerance;
float max_rate[N_AXIS];
float acceleration[N_AXIS];
float max_travel[N_AXIS];
uint8_t pulse_microseconds;
uint8_t step_invert_mask;
uint8_t dir_invert_mask;
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
float junction_deviation;
float arc_tolerance;
uint8_t flags; // Contains default boolean settings
uint8_t homing_dir_mask;
float homing_feed_rate;
float homing_seek_rate;
uint16_t homing_debounce_delay;
float homing_pulloff;
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
uint8_t decimal_places;
float max_velocity[N_AXIS];
float max_travel[N_AXIS];
// uint8_t status_report_mask; // Mask to indicate desired report data.
} settings_t;
extern settings_t settings;
@ -92,6 +92,10 @@ void settings_store_startup_line(uint8_t n, char *line);
// Reads an EEPROM startup line to the protocol line variable
uint8_t settings_read_startup_line(uint8_t n, char *line);
void settings_store_build_info(char *line);
uint8_t settings_read_build_info(char *line);
// Writes selected coordinate data to EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data);

1
sim/.gitignore vendored
View File

@ -1 +1,2 @@
grbl_sim.exe
*.dat

View File

@ -15,11 +15,14 @@
# You should have received a copy of the GNU General Public License
# along with Grbl. If not, see <http://www.gnu.org/licenses/>.
OBJECTS = main.o simulator.o runtime.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o serial.o avr/pgmspace.o avr/interrupt.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o
PLATFORM = WINDOWS
OBJECTS = main.o simulator.o serial.o ../main.o ../protocol.o ../planner.o ../settings.o ../print.o ../nuts_bolts.o eeprom.o ../serial.o avr/pgmspace.o avr/interrupt.o avr/io.o util/delay.o util/floatunsisf.o ../stepper.o ../gcode.o ../spindle_control.o ../motion_control.o ../limits.o ../report.o ../coolant_control.o ../probe.o ../system.o platform_$(PLATFORM).o
CLOCK = 16000000
EXE_NAME = grbl_sim.exe
COMPILE = $(CC) -Wall -Os -DF_CPU=$(CLOCK) -include config.h -I.
COMPILE = $(CC) -Wall -g -DF_CPU=$(CLOCK) -include config.h -I. -DPLAT_$(PLATFORM)
LINUX_LIBRARIES = -lrt -pthread
WINDOWS_LIBRARIES =
# symbolic targets:
all: main
@ -30,15 +33,18 @@ clean:
# file targets:
main: $(OBJECTS)
$(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm
$(COMPILE) -o $(EXE_NAME) $(OBJECTS) -lm $($(PLATFORM)_LIBRARIES)
%.o: %.c
$(COMPILE) -c $< -o $@
../protocol.o: ../protocol.c
$(COMPILE) -include rename_execute_runtime.h -c $< -o $@
$(COMPILE) -c $< -o $@
../planner.o: ../planner.c
$(COMPILE) -include planner_inject_accessors.c -c $< -o $@
../serial.o: ../serial.c
$(COMPILE) -include serial_hooks.h -c $< -o $@
../main.o: ../main.c
$(COMPILE) -include rename_main.h -c $< -o $@

View File

@ -9,3 +9,10 @@ What can you do with Grbl Sim?
- Visualize a g-code program by having the simulator parse and execute to a GUI. Fluctuations in feed rates by the acceleration planner can be viewed as well.
- A powerful debugging tool for development.
- Each of the AVR functions are replaced with dummy functions, like the stepper ISR. These could be written to whatever you need. For example, output simulated step pulses over time and examine its performance.
Realtime modifications by Adam Shelly:
Simulates Atmel hardware in separate thread. Runs in aproximate realtime.
On Linux, use `socat PTY,raw,link=/dev/ttyFAKE,echo=0 "EXEC:'./grbl_sim.exe -n -s step.out -b block.out',pty,raw,echo=0"` to create a fake serial port connected to the simulator. This is useful for testing grbl interface software.

View File

@ -21,22 +21,115 @@
*/
#include "interrupt.h"
#include "io.h"
//pseudo-Interrupt vector table
isr_fp compa_vect[6]={0};
isr_fp compb_vect[6]={0};
isr_fp ovf_vect[6]={0};
void sei() {io.sreg|=SEI;}
void cli() {io.sreg&=~SEI;}
int16_t sim_scaling[8]={0,1,8,64,256,1024,1,1}; //clock scalars
//Timer/Counter modes: these are incomplete, but enough for this application
enum sim_wgm_mode {
wgm_NORMAL,
wgm_CTC,
wgm_FAST_PWM,
wgm_PHASE_PWM,
wgm_PH_F_PWM,
wgm_RESERVED
};
enum sim_wgm_mode sim_wgm0[4] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_CTC,wgm_FAST_PWM};
enum sim_wgm_mode sim_wgmN[8] = {wgm_NORMAL,wgm_PHASE_PWM,wgm_PHASE_PWM,wgm_PH_F_PWM,
wgm_CTC, wgm_FAST_PWM, wgm_FAST_PWM, wgm_FAST_PWM};
void timer_interrupts() {
int i;
uint8_t ien = io.sreg&SEI; //interrupts enabled?
io.prescaler++;
//all clocks
for (i=0;i<2;i++){
uint8_t cs = io.tccrb[i]&7; //clock select bits
int16_t increment = sim_scaling[cs];
//check scaling to see if timer fires
if (increment && (io.prescaler&(increment-1))==0) {
//select waveform generation mode
enum sim_wgm_mode mode;
if (i==0 || i==2) { //(T0 and T2 are different from rest)
uint8_t wgm = io.tccra[i]&3; //look at low 2 bits
mode = sim_wgm0[wgm];
}
else {
uint8_t wgm = ((io.tccrb[i]&8)>>1) | (io.tccra[i]&3); //only using 3 bits for now
mode = sim_wgmN[wgm];
}
//tick
io.tcnt[i]++;
//comparators
if ((io.timsk[i]&(1<<SIM_OCA)) && io.tcnt[i]==io.ocra[i]) io.tifr[i]|=(1<<SIM_OCA);
if ((io.timsk[i]&(1<<SIM_OCB)) && io.tcnt[i]==io.ocrb[i]) io.tifr[i]|=(1<<SIM_OCB);
if ((io.timsk[i]&(1<<SIM_OCC)) && io.tcnt[i]==io.ocrc[i]) io.tifr[i]|=(1<<SIM_OCC);
switch (mode) {
case wgm_NORMAL: //Normal mode
if (i==0) io.tcnt[i]&=0xFF; //timer0 is 8 bit;
if (i==2) io.tcnt[i]&=0xFF; //timer2 is 8 bit;
if (io.tcnt[i]==0) io.tifr[i]|=(1<<SIM_TOV);
break;
case wgm_CTC: //CTC mode
if (io.tcnt[i]==io.ocra[i]) io.tcnt[i]=0;
break;
default: //unsupported
break;
}
//call any triggered interupts
if (ien && io.tifr[i]) {
if (compa_vect[i] && (io.tifr[i]&(1<<SIM_OCA))) {
compa_vect[i]();
io.tifr[i]&=~(1<<SIM_OCA);
//TODO: insert port_monitor call here
}
if (compb_vect[i] && (io.tifr[i]&(1<<SIM_OCB))) {
compb_vect[i]();
io.tifr[i]&=~(1<<SIM_OCB);
}
if (ovf_vect[i] && (io.tifr[i]&(1<<SIM_TOV))) {
ovf_vect[i]();
io.tifr[i]&=~(1<<SIM_TOV);
}
}
}
}
//// TODO for more complete timer sim.
// pwm modes. (only used for variable spindle, I think).
// -- would require fixing wgm mode for Timers1..5
// -- phase correct modes need updown counter.
// output pins (also only for variable spindle, I think).
//// Other chip features not needed yet for grbl:
// writes to TCNT0 prevent compare match (need write detector.)
// force output compare (unused)
// input capture (unused and how would we signal it?)
// define the other output compare registers.
// usercode can clear unhandled interrupt flags by writing 1.
// --(this may be impossible, since bit was 1 before the write.)
// prescaler reset.
// maybe need to cli on interrupt entry
}
// dummy register variables
uint16_t timsk0;
uint16_t timsk1;
uint16_t timsk2;
uint16_t ocr1a;
uint16_t ocr2a;
uint16_t tcnt0;
uint16_t tcnt2;
uint16_t tccr0b;
uint16_t tccr2b;
uint16_t tccr1b;
uint16_t tccr0a;
uint16_t tccr1a;
uint16_t tccr2a;
uint16_t pcmsk0;
uint16_t pcicr;
void sei() {};

View File

@ -24,59 +24,33 @@
#ifndef interrupt_h
#define interrupt_h
#include <inttypes.h>
// dummy register variables
extern uint16_t timsk0;
extern uint16_t timsk1;
extern uint16_t timsk2;
extern uint16_t tcnt0;
extern uint16_t tcnt2;
extern uint16_t tccr0b;
extern uint16_t tccr0a;
extern uint16_t tccr2a;
extern uint16_t tccr2b;
extern uint16_t tccr1b;
extern uint16_t tccr1a;
extern uint16_t ocr1a;
extern uint16_t ocr2a;
extern uint16_t pcmsk0;
extern uint16_t pcicr;
// macros to turn avr interrupts into regular functions
#define TIMER1_COMPA_vect
//#define TIMER1_COMPA_vect
#define ISR(a) void interrupt_ ## a ()
// enable interrupts does nothing in the simulation environment
void sei();
// Stub of the timer interrupt functions we need
void interrupt_TIMER0_COMPA_vect();
void interrupt_TIMER1_COMPA_vect();
void interrupt_TIMER0_OVF_vect();
void interrupt_SERIAL_UDRE();
void interrupt_SERIAL_RX();
//pseudo-Interrupt vector table
typedef void(*isr_fp)(void);
extern isr_fp compa_vect[6];
extern isr_fp compb_vect[6];
extern isr_fp ovf_vect[6];
// enable interrupts now does something in the simulation environment
#define SEI 0x80
void sei();
void cli();
//simulate timer operation
void timer_interrupts();
// dummy macros for interrupt related registers
#define TIMSK0 timsk0
#define TIMSK1 timsk1
#define TIMSK2 timsk2
#define OCR1A ocr1a
#define OCR2A ocr2a
#define OCIE1A 0
#define OCIE2A 0
#define TCNT0 tcnt0
#define TCNT2 tcnt2
#define TCCR0B tccr0b
#define TCCR0A tccr0a
#define TCCR1A tccr1a
#define TCCR1B tccr1b
#define TCCR2A tccr2a
#define TCCR2B tccr2b
#define CS21 0
#define CS10 0
#define WGM13 0
#define WGM12 0
#define WGM11 0
#define WGM10 0
#define WGM21 0
#define COM1A0 0
#define COM1B0 0
#define TOIE0 0
#define TOIE2 0
#define PCICR pcicr
#endif

4
sim/avr/io.c Normal file
View File

@ -0,0 +1,4 @@
#include "io.h"
// dummy register variables
volatile io_sim_t io={{0}};

View File

@ -1,5 +1,6 @@
/*
io.h - dummy replacement for the avr include of the same name
interrupt.h - replacement for the avr include of the same name to provide
dummy register variables and macros
Part of Grbl Simulator
@ -19,3 +20,182 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef io_h
#define io_h
#include <inttypes.h>
union hilo16 {
uint16_t w;
struct {
uint8_t l; //TODO: check that these are right order on x86. Doesn't matter for current usage, but might someday
uint8_t h;
};
};
enum {
SIM_A, SIM_B, SIM_C, SIM_D, SIM_E,
SIM_F, SIM_G, SIM_H, SIM_J, SIM_K, SIM_L,
SIM_PORT_COUNT
};
#define SIM_N_TIMERS 6
// dummy register variables
typedef struct io_sim {
uint8_t ddr[SIM_PORT_COUNT];
uint8_t port[SIM_PORT_COUNT];
uint8_t pin[SIM_PORT_COUNT];
uint8_t timsk[SIM_N_TIMERS];
uint16_t ocra[SIM_N_TIMERS];
uint16_t ocrb[SIM_N_TIMERS];
uint16_t ocrc[SIM_N_TIMERS];
uint16_t tcnt[SIM_N_TIMERS]; //tcint0 is really only 8bit
uint8_t tccra[SIM_N_TIMERS];
uint8_t tccrb[SIM_N_TIMERS];
uint8_t tifr[SIM_N_TIMERS];
uint8_t pcicr;
uint8_t pcmsk[3];
uint8_t ucsr0[3];
uint8_t udr[3];
union hilo16 ubrr0;
uint16_t prescaler; //continuously running
uint8_t sreg;
} io_sim_t;
volatile extern io_sim_t io;
// dummy macros for interrupt related registers
#define PORTA io.port[SIM_A]
#define PORTB io.port[SIM_B]
#define PORTC io.port[SIM_C]
#define PORTD io.port[SIM_D]
#define PORTE io.port[SIM_E]
#define PORTF io.port[SIM_F]
#define PORTG io.port[SIM_G]
#define PORTH io.port[SIM_H]
#define PORTJ io.port[SIM_J]
#define PORTK io.port[SIM_K]
#define PORTL io.port[SIM_L]
#define DDRA io.ddr[SIM_A]
#define DDRB io.ddr[SIM_B]
#define DDRC io.ddr[SIM_C]
#define DDRD io.ddr[SIM_D]
#define DDRE io.ddr[SIM_E]
#define DDRF io.ddr[SIM_F]
#define DDRG io.ddr[SIM_G]
#define DDRH io.ddr[SIM_H]
#define DDRJ io.ddr[SIM_J]
#define PINA io.pin[SIM_A]
#define PINB io.pin[SIM_B]
#define PINC io.pin[SIM_C]
#define PIND io.pin[SIM_D]
#define PINE io.pin[SIM_E]
#define PINF io.pin[SIM_F]
#define PING io.pin[SIM_G]
#define PINH io.pin[SIM_H]
#define PINJ io.pin[SIM_J]
#define PINK io.pin[SIM_K]
#define PINL io.pin[SIM_L]
#define TIMSK0 io.timsk[0]
#define TIMSK1 io.timsk[1]
#define TIMSK2 io.timsk[2]
#define TIMSK3 io.timsk[3]
#define TIMSK4 io.timsk[4]
#define TIMSK5 io.timsk[5]
#define SIM_TOV 0
#define SIM_OCA 1
#define SIM_OCB 2
#define SIM_OCC 3
#define SIM_ICI 5
#define OCIE0A SIM_OCA
#define OCIE0B SIM_OCB
#define TOIE0 SIM_TOV
#define ICIE1 SIM_ICI
#define OCIE1C SIM_OCC
#define OCIE1B SIM_OCB
#define OCIE1A SIM_OCA
#define TOIE1 SIM_ICI
#define ICIE2 SIM_ICI
#define OCIE2C SIM_OCC
#define OCIE2B SIM_OCB
#define OCIE2A SIM_OCA
#define TOIE2 SIM_TOV
#define OCR0A io.ocra[0]
#define OCR1A io.ocra[1]
#define OCR2A io.ocra[2]
//There are more..
#define TCNT0 io.tcnt[0]
#define TCNT1 io.tcnt[1]
#define TCNT2 io.tcnt[2]
#define TCCR0B io.tccra[0]
#define TCCR0A io.tccrb[0]
#define TCCR1A io.tccra[1]
#define TCCR1B io.tccrb[1]
#define TCCR2A io.tccra[2]
#define TCCR2B io.tccrb[2]
#define CS00 0
#define CS01 1
#define CS12 2
#define CS11 1
#define CS10 0
#define CS21 1
#define WGM13 4
#define WGM12 3
#define WGM11 1
#define WGM10 0
#define WGM21 1
#define COM1A1 7
#define COM1A0 6
#define COM1B1 5
#define COM1B0 4
#define COM1C1 3
#define COM1C0 2
#define PCICR io.pcicr
#define PCIE0 0
#define PCIE1 1
//serial channel
#define UCSR0A io.ucsr0[SIM_A]
#define UCSR0B io.ucsr0[SIM_B]
#define UDR0 io.udr[0]
#define UDRIE0 0
#define RXCIE0 1
#define RXEN0 2
#define TXEN0 3
#define U2X0 4
#define UBRR0H io.ubrr0.h
#define UBRR0L io.ubrr0.l
#define PCMSK0 io.pcmsk[0]
#define PCMSK1 io.pcmsk[1]
#define PCMSK2 io.pcmsk[2]
#endif

1
sim/avr/wdt.h Normal file
View File

@ -0,0 +1 @@
uint16_t wdt;

View File

@ -24,94 +24,5 @@
#include "../config.h"
#include <inttypes.h>
// dummy register variables implemented in simulator.c
extern uint8_t stepping_ddr;
extern uint8_t stepping_port;
extern uint8_t spindle_ddr;
extern uint8_t spindle_port;
extern uint8_t limit_ddr;
extern uint8_t limit_port;
extern uint8_t limit_int_reg;
extern uint8_t pinout_ddr;
extern uint8_t pinout_port;
extern uint8_t pinout_int_reg;
extern uint8_t coolant_flood_ddr;
extern uint8_t coolant_flood_port;
// ReDefine pin-assignments
#undef STEPPING_DDR
#define STEPPING_DDR stepping_ddr
#undef STEPPING_PORT
#define STEPPING_PORT stepping_port
#undef X_STEP_BIT
#define X_STEP_BIT 2 // Uno Digital Pin 2
#undef Y_STEP_BIT
#define Y_STEP_BIT 3 // Uno Digital Pin 3
#undef Z_STEP_BIT
#define Z_STEP_BIT 4 // Uno Digital Pin 4
#undef X_DIRECTION_BIT
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#undef Y_DIRECTION_BIT
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#undef Z_DIRECTION_BIT
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#undef STEPPERS_DISABLE_DDR
#define STEPPERS_DISABLE_DDR stepping_ddr
#undef STEPPERS_DISABLE_PORT
#define STEPPERS_DISABLE_PORT stepping_port
#undef STEPPERS_DISABLE_BIT
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#undef LIMIT_DDR
#define LIMIT_DDR limit_ddr
#undef LIMIT_PORT
#define LIMIT_PORT limit_port
#undef LIMIT_PIN
#define LIMIT_PIN limit_port
#undef X_LIMIT_BIT
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#undef Y_LIMIT_BIT
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#undef Z_LIMIT_BIT
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#undef LIMIT_INT
#define LIMIT_INT 0
#undef LIMIT_PCMSK
#define LIMIT_PCMSK limit_int_reg
#undef SPINDLE_ENABLE_DDR
#define SPINDLE_ENABLE_DDR spindle_ddr
#undef SPINDLE_ENABLE_PORT
#define SPINDLE_ENABLE_PORT spindle_port
#undef SPINDLE_ENABLE_BIT
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#undef SPINDLE_DIRECTION_DDR
#define SPINDLE_DIRECTION_DDR spindle_ddr
#undef SPINDLE_DIRECTION_PORT
#define SPINDLE_DIRECTION_PORT spindle_port
#undef SPINDLE_DIRECTION_BIT
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13
#undef PINOUT_DDR
#define PINOUT_DDR pinout_ddr
#undef PINOUT_PORT
#define PINOUT_PORT pinout_port
#undef PINOUT_PIN
#define PINOUT_PIN pinout_port
#undef PINOUT_PCMSK
#define PINOUT_PCMSK pinout_int_reg
#undef PINOUT_INT
#define PINOUT_INT 0
#undef COOLANT_FLOOD_DDR
#define COOLANT_FLOOD_DDR coolant_flood_ddr
#undef COOLANT_FLOOD_PORT
#define COOLANT_FLOOD_PORT coolant_flood_port
#undef COOLANT_FLOOD_BIT
#define COOLANT_FLOOD_BIT 0
#define AUTO_REPORT_MOVE_DONE
#endif

View File

@ -21,18 +21,75 @@
*/
// These are never called in the simulator
#include <stdio.h>
#define MAX_EEPROM_SIZE 4096 //4KB EEPROM in Mega
FILE* eeprom_create_empty_file(){
FILE* fp = fopen("EEPROM.DAT","w+b");
int i;
if (fp){
for(i=0;i<MAX_EEPROM_SIZE;i++){
fputc(0,fp);
}
fseek(fp,0,SEEK_SET);
}
return fp;
}
FILE* eeprom_fp(){
static FILE* EEPROM_FP = NULL;
static int tried= 0;
if (!EEPROM_FP && !tried){
tried = 1;
EEPROM_FP = fopen("EEPROM.DAT","r+b");
if (!EEPROM_FP) {
EEPROM_FP = eeprom_create_empty_file();
}
}
return EEPROM_FP;
}
void eeprom_close(){
FILE* fp = eeprom_fp();
fclose(fp);
}
unsigned char eeprom_get_char( unsigned int addr ) {
return 0;
FILE* fp = eeprom_fp();
if (fseek(fp,addr,SEEK_SET)) { return 0; } //no such address
return fgetc(fp);
}
void eeprom_put_char( unsigned int addr, unsigned char new_value ) {
FILE* fp = eeprom_fp();
if (fseek(fp,addr,SEEK_SET)) { return; } //no such address
fputc(new_value, fp);
}
// Extensions added as part of Grbl
// KEEP IN SYNC WITH ../eeprom.c
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
unsigned char checksum = 0;
for(; size > 0; size--) {
checksum = (checksum << 1) || (checksum >> 7);
checksum += *source;
eeprom_put_char(destination++, *(source++));
}
eeprom_put_char(destination, checksum);
}
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) {
return 0;
unsigned char data, checksum = 0;
for(; size > 0; size--) {
data = eeprom_get_char(source++);
checksum = (checksum << 1) || (checksum >> 7);
checksum += data;
*(destination++) = data;
}
return(checksum == eeprom_get_char(source));
}
// end of file

3
sim/eeprom.h Normal file
View File

@ -0,0 +1,3 @@
#include "../eeprom.h"
void eeprom_close();

View File

@ -34,18 +34,106 @@
#include "simulator.h"
arg_vars_t args;
const char* progname;
int usage(const char* badarg){
if (badarg){
printf("Unrecognized option %s\n",badarg);
}
printf("Usage: \n"
"%s [options] [time_step] [block_file]\n"
" Options:\n"
" -r <report time> : minimum time step for printing stepper values. Default=0=no print.\n"
" -t <time factor> : multiplier to realtime clock. Default=1. (needs work)\n"
" -g <response file> : file to report responses from grbl. default = stdout\n"
" -b <block file> : file to report each block executed. default = stdout\n"
" -s <step file> : file to report each step executed. default = stderr\n"
" -c<comment_char> : character to print before each line from grbl. default = '#'\n"
" -n : no comments before grbl response lines.\n"
" -h : this help.\n"
"\n <time_step> and <block_file> can be specifed with option flags or positional parameters\n"
"\n ^-F to shutdown cleanly\n\n",
progname);
return -1;
}
//prototype for renamed original main function
int avr_main(void);
//wrapper for thread interface
PLAT_THREAD_FUNC(avr_main_thread,exit){
avr_main();
return NULL;
}
int main(int argc, char *argv[]) {
uint32_t tick_rate=1;
int positional_args=0;
//defaults
args.step_out_file = stderr;
args.block_out_file = stdout;
args.grbl_out_file = stdout;
args.comment_char = '#';
args.step_time = 0.0;
// Get the minimum time step for printing stepper values.
// If not given or the command line cannot be parsed to a float than
// step_time= 0.0; This means to not print stepper values at all
if(argc>1) {
argv++;
step_time= atof(*argv);
progname = argv[0];
while (argc>1) {
argv++;argc--;
if (argv[0][0] == '-'){
switch(argv[0][1]){
case 'c': //set Comment char
args.comment_char = argv[0][2];
break;
case 'n': //No comment char on grbl responses
args.comment_char = 0;
break;
case 't': //Tick rate
argv++;argc--;
tick_rate = atof(*argv);
break;
case 'b': //Block file
argv++;argc--;
args.block_out_file = fopen(*argv,"w");
break;
case 's': //Step out file.
argv++;argc--;
args.step_out_file = fopen(*argv,"w");
break;
case 'g': //Grbl output
argv++;argc--;
args.grbl_out_file = fopen(*argv,"w");
break;
case 'r': //step_time for Reporting
argv++;argc--;
args.step_time= atof(*argv);
break;
case 'h':
return usage(NULL);
default:
return usage(*argv);
}
}
else { //handle old positional argument interface
positional_args++;
switch(positional_args){
case 1:
args.step_time= atof(*argv);
break;
case 2: //block out and grbl out to same file, like before.
args.block_out_file = fopen(*argv,"w");
args.grbl_out_file = args.block_out_file;
break;
default:
return usage(*argv);
}
}
}
// Setup output file handles. Block info goes to stdout. Stepper values go to stderr.
block_out_file= stdout;
step_out_file= stderr;
// Make sure the output streams are flushed immediately.
// This is important when using the simulator inside another application in parallel
// to the real grbl.
@ -53,36 +141,26 @@ int main(int argc, char *argv[]) {
// does not know line buffered streams. So for now we stick to flushing every character.
//setvbuf(stdout, NULL, _IONBF, 1);
//setvbuf(stderr, NULL, _IONBF, 1);
st_init(); // Setup stepper pins and interrupt timers
memset(&sys, 0, sizeof(sys)); // Clear all system variables
//settings_reset(); TODO: implement read_settings from eeprom
settings_init();
protocol_init(); // Clear incoming line data
//printf("plan_init():\n");
plan_init(); // Clear block buffer and planner variables
//printf("gc_init():\n");
gc_init(); // Set g-code parser to default state
//printf("spindle_init():\n");
spindle_init();
//printf("limits_init():\n");
limits_init();
//printf("coolant_init():\n");
coolant_init();
//printf("st_reset():\n");
st_reset(); // Clear stepper subsystem variables.
sys.auto_start = true; // runtime commands are not processed. We start to simulate immediately
// Main loop of command processing until EOF is encountered
//printf("protocol_process():\n");
protocol_process();
// flush the block buffer and print stepper info on any pending blocks
plan_synchronize();
//( Files are now closed cleanly when sim gets EOF or CTRL-F.)
platform_init();
init_simulator(tick_rate);
//launch a thread with the original grbl code.
plat_thread_t*th = platform_start_thread(avr_main_thread);
if (!th){
printf("Fatal: Unable to start hardware thread.\n");
exit(-5);
}
//All the stream io and interrupt happen in this thread.
sim_loop();
platform_kill_thread(th); //need force kill since original main has no return.
// Graceful exit
fclose(block_out_file);
fclose(step_out_file);
shutdown_simulator(0);
platform_terminate();
exit(EXIT_SUCCESS);
}

View File

@ -1,10 +1,10 @@
#include "../planner.h"
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
block_t *get_block_buffer() { return block_buffer; }
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
plan_block_t *get_block_buffer() { return block_buffer; }
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
static uint8_t block_buffer_head; // Index of the next block to be pushed
uint8_t get_block_buffer_head() { return block_buffer_head; }
static volatile uint8_t block_buffer_tail; // Index of the next block to be pushed
static uint8_t block_buffer_tail; // Index of the next block to be pushed
uint8_t get_block_buffer_tail() { return block_buffer_tail; }

23
sim/platform.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef platform_h
#ifdef PLAT_LINUX
#include "platform_linux.h"
#else
#include "platform_windows.h"
#endif
#define platform_h
void platform_init();
void platform_terminate();
plat_thread_t* platform_start_thread(plat_threadfunc_t func);
void platform_stop_thread(plat_thread_t* thread);
void platform_kill_thread(plat_thread_t* thread);
uint32_t platform_ns(); //monotonically increasing nanoseconds since program start.
void platform_sleep(long microsec); //sleep for suggested time in microsec.
uint8_t platform_poll_stdin(); //non-blocking stdin read - returns 0 if no char present, 0xFF for EOF
#endif

116
sim/platform_LINUX.c Normal file
View File

@ -0,0 +1,116 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <time.h>
#include <sys/time.h>
#include "platform.h"
#define MS_PER_SEC 1000000
//any platform-specific setup that must be done before sim starts here
void platform_init()
{
}
//cleanup int here;
void platform_terminate()
{
}
//returns a free-running 32 bit nanosecond counter which rolls over
uint32_t platform_ns()
{
static uint32_t gTimeBase = 0;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
if (gTimeBase== 0){gTimeBase=ts.tv_nsec;}
return ts.tv_nsec-gTimeBase;
}
//sleep in microseconds
void platform_sleep(long microsec)
{
struct timespec ts={0};
while (microsec >= MS_PER_SEC){
ts.tv_sec++;
microsec-=MS_PER_SEC;
}
ts.tv_nsec = microsec*1000;
nanosleep(&ts,NULL);
}
#define SIM_ECHO_TERMINAL 1 //use this to make grbl_sim act like a serial terminal with local echo on.
//set terminal to allow kbhit detection
void enable_kbhit(int dir)
{
static struct termios oldt, newt;
if ( dir == 1 )
{
tcgetattr( STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON );
if (!SIM_ECHO_TERMINAL) {
newt.c_lflag &= ~( ECHO );
}
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
}
else
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
}
//detect key pressed
int kbhit (void)
{
struct timeval tv={0};
fd_set rdfs={{0}};
int retval;
/* tv.tv_sec = 0; */
/* tv.tv_usec = 0; */
/* FD_ZERO(&rdfs); */
FD_SET (STDIN_FILENO, &rdfs);
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
retval = FD_ISSET(STDIN_FILENO, &rdfs);
return retval;
}
plat_thread_t* platform_start_thread(plat_threadfunc_t threadfunc) {
plat_thread_t* th = malloc(sizeof(plat_thread_t));
if (pthread_create(&th->tid, NULL, threadfunc, &th->exit)){
free(th);
return NULL;
}
return th;
}
//ask thread to exit nicely, wait
void platform_stop_thread(plat_thread_t* th){
th->exit = 1;
pthread_join(th->tid,NULL);
}
//force-kill thread
void platform_kill_thread(plat_thread_t* th){
th->exit = 1;
pthread_cancel(th->tid);
}
//return char if one available.
uint8_t platform_poll_stdin() {
uint8_t char_in=0;
enable_kbhit(1);
if ( kbhit()) {
char_in = getchar();
}
enable_kbhit(0);
return char_in;
}

75
sim/platform_WINDOWS.c Normal file
View File

@ -0,0 +1,75 @@
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include "platform.h"
#define NS_PER_SEC 1000000000
#define MICRO_PER_MILLI 1000
double ns_per_perfcount;
//any platform-specific setup that must be done before sim starts here
void platform_init()
{
__int64 counts_per_sec;
QueryPerformanceFrequency((LARGE_INTEGER*)&counts_per_sec);
ns_per_perfcount = (float)NS_PER_SEC / counts_per_sec;
}
//cleanup int here;
void platform_terminate()
{
}
//returns a free-running 32 bit nanosecond counter which rolls over
uint32_t platform_ns()
{
static uint32_t gTimeBase = 0;
__int64 counts;
uint32_t ns;
QueryPerformanceCounter((LARGE_INTEGER*)&counts);
ns = (counts*ns_per_perfcount);
if (gTimeBase== 0){gTimeBase=ns;}
return ns-gTimeBase;
}
//sleep in microseconds
void platform_sleep(long microsec)
{
Sleep(microsec/MICRO_PER_MILLI);
}
//create a thread
plat_thread_t* platform_start_thread(plat_threadfunc_t threadfunc) {
plat_thread_t* th = malloc(sizeof(plat_thread_t));
th->tid = CreateThread(NULL,0,threadfunc,&th->exit,0,NULL);
if (!th->tid){
free(th);
return NULL;
}
return th;
}
//ask thread to exit nicely, wait
void platform_stop_thread(plat_thread_t* th){
th->exit = 1;
WaitForSingleObject(th->tid,INFINITE);
}
//force-kill thread
void platform_kill_thread(plat_thread_t* th){
th->exit = 1;
TerminateThread(th->tid, 0);
}
//return char if one available.
uint8_t platform_poll_stdin() {
if (_kbhit()) {
return getch();
}
return 0;
}

16
sim/platform_linux.h Normal file
View File

@ -0,0 +1,16 @@
#ifdef platform_h
#error "platform implementation already defined"
#else
#include <pthread.h>
typedef struct {
pthread_t tid;
int exit;
} plat_thread_t;
typedef void*(*plat_threadfunc_t)(void*);
#define PLAT_THREAD_FUNC(name,arg) void* name(void* arg)
#endif

16
sim/platform_windows.h Normal file
View File

@ -0,0 +1,16 @@
#ifdef platform_h
#error "platform implementation already defined"
#else
#include <windows.h>
typedef struct {
HANDLE tid;
int exit;
} plat_thread_t;
typedef DWORD WINAPI(*plat_threadfunc_t)(LPVOID);
#define PLAT_THREAD_FUNC(name,arg) DWORD WINAPI name(LPVOID arg)
#endif

View File

@ -1,2 +0,0 @@
#define protocol_execute_runtime orig_protocol_execute_runtime
//void __attribute__((weak)) protocol_execute_runtime(void);

1
sim/rename_main.h Normal file
View File

@ -0,0 +1 @@
#define main avr_main

View File

@ -1,38 +0,0 @@
/*
runtime.c - replacement for the modul of the same name in grbl
Run time commands are not processed in the simulator.
Instead, the execute_runtime() is used as a hook to handle stepper simulation
and printing of simulation results.
Part of Grbl Simulator
Copyright (c) 2012 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "simulator.h"
#include <stdio.h>
void orig_protocol_execute_runtime(void);
// replacement for original execute_runtime as a hook to print blocks as they are generated
// and to control simulation of buffered blocks
void protocol_execute_runtime(void) {
orig_protocol_execute_runtime();
//printf("printBlock():\n");
printBlock();
//printf("handle_buffer():\n");
handle_buffer();
}

View File

@ -26,28 +26,49 @@
#include "simulator.h"
#include <stdio.h>
void serial_write(uint8_t data) {
printBlock();
if(print_comment && data!='\n' && data!='\r') {
fprintf(block_out_file, "# ");
print_comment= 0;
}
if(data=='\n' || data=='\r')
print_comment= 1;
fprintf(block_out_file, "%c", data);
// Indicate the end of processing a command. See simulator.c for details
runtime_second_call= 0;
}
//prototypes for overridden functions
uint8_t orig_serial_read();
//used to inject a sleep in grbl main loop,
// ensures hardware simulator gets some cycles in "parallel"
uint8_t serial_read() {
int c;
if((c = fgetc(stdin)) != EOF) {
serial_write(c);
return c;
}
return SERIAL_NO_DATA;
platform_sleep(0);
return orig_serial_read();
}
void simulate_write_interrupt(){
while (UCSR0B & (1<<UDRIE0)){
interrupt_SERIAL_UDRE();
grbl_out(UDR0);
}
}
void simulate_read_interrupt(){
uint8_t char_in = platform_poll_stdin();
if (char_in) {
UDR0 = char_in;
//EOF or CTRL-F to exit
if (UDR0 == EOF || UDR0 == 0xFF || UDR0 == 0x06 ) {
sim.exit = 1;
}
//debugging
if (UDR0 == '%') {
printf("%ld %f\n",sim.masterclock,(double)sim.sim_time);
}
interrupt_SERIAL_RX();
}
}
extern volatile uint8_t rx_buffer_head;
extern volatile uint8_t rx_buffer_tail;
void simulate_serial(){
simulate_write_interrupt();
uint8_t head = rx_buffer_head+1;
if (head==RX_BUFFER_SIZE) { head = 0; }
if (head!=rx_buffer_tail) {
simulate_read_interrupt();
}
}

2
sim/serial_hooks.h Normal file
View File

@ -0,0 +1,2 @@
#define serial_read orig_serial_read

2
sim/sim.sh Executable file
View File

@ -0,0 +1,2 @@
./grbl_sim.exe 0.01 <HelloWorld.nc >HelloWorld.dat 2> HelloWorldSteps.dat
gnuplot -persist gnuplot.plt

View File

@ -28,102 +28,146 @@
#include "../planner.h"
#include "../nuts_bolts.h"
#include "simulator.h"
// Derived from OCR2A set during stepper init. Not simplified because
// OCR2A formula results in an unsigned int, which results in a slightly
// different value than simplifying to 1 / ISR_TICKS_PER_SECOND.
#define ISR_INTERVAL ((F_CPU/ISR_TICKS_PER_SECOND)/8) * 8.0 / F_CPU
// This variable is needed to determine if execute_runtime() is called in a loop
// waiting for the buffer to empty, as in plan_synchronize()
// it is reset in serial_write() because this is certainly called at the end of
// every command processing
int runtime_second_call= 0;
#include "avr/interrupt.h" //for registers and isr declarations.
#include "eeprom.h"
// Current time of the stepper simulation
double sim_time= 0.0;
// Next time the status of stepper values should be printed
double next_print_time= 0.0;
// Minimum time step for printing stepper values. Given by user via command line
double step_time= 0.0;
// global system variable structure for position etc.
system_t sys;
int block_position[]= {0,0,0};
uint8_t print_comment= 1;
uint8_t end_of_block= 1;
int block_position[]= {0,0,0}; //step count after most recently planned block
uint32_t block_number= 0;
// Output file handles set by main program
FILE *block_out_file;
FILE *step_out_file;
sim_vars_t sim={0};
// dummy port variables
uint8_t stepping_ddr;
uint8_t stepping_port;
uint8_t spindle_ddr;
uint8_t spindle_port;
uint8_t limit_ddr;
uint8_t limit_port;
uint8_t limit_int_reg;
uint8_t pinout_ddr;
uint8_t pinout_port;
uint8_t pinout_int_reg;
uint8_t coolant_flood_ddr;
uint8_t coolant_flood_port;
extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
extern uint8_t block_buffer_head; // Index of the next block to be pushed
extern uint8_t block_buffer_tail; // Index of the block to process now
//local prototypes
void print_steps(bool force);
// Stub of the timer interrupt function from stepper.c
void interrupt_TIMER2_COMPA_vect();
//setup
void init_simulator(float time_multiplier) {
// Call the stepper interrupt until one block is finished
void sim_stepper() {
//printf("sim_stepper()\n");
block_t *current_block= plan_get_current_block();
//register the interrupt handlers we actually use.
compa_vect[1] = interrupt_TIMER1_COMPA_vect;
ovf_vect[0] = interrupt_TIMER0_OVF_vect;
#ifdef STEP_PULSE_DELAY
compa_vect[0] = interrupt_TIMER0_COMPA_vect;
#endif
// If the block buffer is empty, call the stepper interrupt one last time
// to let it handle sys.cycle_start etc.
if(current_block==NULL) {
interrupt_TIMER2_COMPA_vect();
sim_time+= ISR_INTERVAL;
return;
}
sim.next_print_time = args.step_time;
sim.speedup = time_multiplier;
sim.baud_ticks = (int)((double)F_CPU*8/BAUD_RATE); //ticks per byte
}
sys.state = STATE_CYCLE;
while(current_block==plan_get_current_block()) {
interrupt_TIMER2_COMPA_vect();
sim_time+= ISR_INTERVAL;
// Check to see if we should print some info
if(step_time>0.0) {
if(sim_time>=next_print_time) {
if(end_of_block) {
end_of_block= 0;
fprintf(step_out_file, "# block number %d\n", block_number);
}
fprintf(step_out_file, "%20.15f, %d, %d, %d\n", sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
//shutdown simulator - close open files
int shutdown_simulator(uint8_t exitflag) {
fclose(args.block_out_file);
print_steps(1);
fclose(args.step_out_file);
fclose(args.grbl_out_file);
eeprom_close();
return 1/(!exitflag); //force exception, since avr_main() has no returns.
}
// Make sure the simulation time doesn't get ahead of next_print_time
while(next_print_time<sim_time) next_print_time+= step_time;
}
}
}
// always print stepper values at the end of a block
if(step_time>0.0) {
fprintf(step_out_file, "%20.15f, %d, %d, %d\n", sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
end_of_block= 1;
block_number++;
void simulate_hardware(bool do_serial){
//do one tick
sim.masterclock++;
sim.sim_time = (float)sim.masterclock/F_CPU;
timer_interrupts();
if (do_serial) simulate_serial();
//TODO:
// check limit pins, call pinchange interrupt if enabled
// can ignore pinout int vect - hw start/hold not supported
}
//runs the hardware simulator at the desired rate until sim.exit is set
void sim_loop(){
uint64_t simulated_ticks=0;
uint32_t ns_prev = platform_ns();
uint64_t next_byte_tick = F_CPU; //wait 1 sec before reading IO.
while (!sim.exit || sys.state>2 ) { //don't quit until idle
if (sim.speedup) {
//calculate how many ticks to do.
uint32_t ns_now = platform_ns();
uint32_t ns_elapsed = (ns_now-ns_prev)*sim.speedup; //todo: try multipling nsnow
simulated_ticks += F_CPU/1e9*ns_elapsed;
ns_prev = ns_now;
}
else {
simulated_ticks++; //as fast as possible
}
while (sim.masterclock < simulated_ticks){
//only read serial port as fast as the baud rate allows
bool read_serial = (sim.masterclock >= next_byte_tick);
//do low level hardware
simulate_hardware(read_serial);
//print the steps.
//For further decoupling, could maintain own counter of STEP_PORT pulses,
// print that instead of sys.position.
print_steps(0);
if (read_serial){
next_byte_tick+=sim.baud_ticks;
//recent block can only change after input, so check here.
printBlock();
}
//TODO:
// set limit pins based on position,
// set probe pin when probing.
// if VARIABLE_SPINDLE, measure pwm pin to report speed?
}
platform_sleep(0); //yield
}
}
//show current position in steps
void print_steps(bool force)
{
static plan_block_t* printed_block = NULL;
plan_block_t* current_block = plan_get_current_block();
if (sim.next_print_time == 0.0) { return; } //no printing
if (current_block != printed_block ) {
//new block.
if (block_number) { //print values from the end of prev block
fprintf(args.step_out_file, "%20.15f %d, %d, %d\n", sim.sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
}
printed_block = current_block;
if (current_block == NULL) { return; }
// print header
fprintf(args.step_out_file, "# block number %d\n", block_number++);
}
//print at correct interval while executing block
else if ((current_block && sim.sim_time>=sim.next_print_time) || force ) {
fprintf(args.step_out_file, "%20.15f %d, %d, %d\n", sim.sim_time, sys.position[X_AXIS], sys.position[Y_AXIS], sys.position[Z_AXIS]);
fflush(args.step_out_file);
//make sure the simulation time doesn't get ahead of next_print_time
while (sim.next_print_time<=sim.sim_time) sim.next_print_time += args.step_time;
}
}
//Functions for peeking inside planner state:
plan_block_t *get_block_buffer();
uint8_t get_block_buffer_head();
uint8_t get_block_buffer_tail();
// Returns the index of the previous block in the ring buffer
uint8_t prev_block_index(uint8_t block_index)
{
@ -132,62 +176,48 @@ uint8_t prev_block_index(uint8_t block_index)
return(block_index);
}
block_t *get_block_buffer();
uint8_t get_block_buffer_head();
uint8_t get_block_buffer_tail();
block_t *plan_get_recent_block() {
plan_block_t *plan_get_recent_block() {
if (get_block_buffer_head() == get_block_buffer_tail()) { return(NULL); }
return(get_block_buffer()+prev_block_index(get_block_buffer_head()));
}
// Print information about the most recently inserted block
// but only once!
void printBlock() {
block_t *b;
static block_t *last_block;
//printf("printBlock()\n");
plan_block_t *b;
static plan_block_t *last_block;
b= plan_get_recent_block();
if(b!=last_block && b!=NULL) {
//fprintf(block_out_file,"%s\n", line);
//fprintf(block_out_file," block: ");
if(b->direction_bits & (1<<X_DIRECTION_BIT)) block_position[0]-= b->steps_x;
else block_position[0]+= b->steps_x;
fprintf(block_out_file,"%d, ", block_position[0]);
if(b->direction_bits & (1<<Y_DIRECTION_BIT)) block_position[1]-= b->steps_y;
else block_position[1]+= b->steps_y;
fprintf(block_out_file,"%d, ", block_position[1]);
if(b->direction_bits & (1<<Z_DIRECTION_BIT)) block_position[2]-= b->steps_z;
else block_position[2]+= b->steps_z;
fprintf(block_out_file,"%d, ", block_position[2]);
fprintf(block_out_file,"%f", b->entry_speed_sqr);
fprintf(block_out_file,"\n");
int i;
for (i=0;i<N_AXIS;i++){
if(b->direction_bits & get_direction_mask(i)) block_position[i]-= b->steps[i];
else block_position[i]+= b->steps[i];
fprintf(args.block_out_file,"%d, ", block_position[i]);
}
fprintf(args.block_out_file,"%f", b->entry_speed_sqr);
fprintf(args.block_out_file,"\n");
fflush(args.block_out_file); //TODO: needed?
last_block= b;
}
}
// The simulator assumes that grbl is fast enough to keep the buffer full.
// Thus, the stepper interrupt is only called when the buffer is full and then only to
// finish one block.
// Only when plan_synchronize() wait for the whole buffer to clear, the stepper interrupt
// to finish all pending moves.
void handle_buffer() {
// runtime_second_call is reset by serial_write() after every command.
// Only when execute_runtime() is called repeatedly by plan_synchronize()
// runtime_second_call will be incremented above 2
//printf("handle_buffer()\n");
if(plan_check_full_buffer() || runtime_second_call>2) {
sim_stepper(step_out_file);
} else {
runtime_second_call++;
//printer for grbl serial port output
void grbl_out(uint8_t data){
static uint8_t buf[128]={0};
static uint8_t len=0;
static bool continuation = 0;
buf[len++]=data;
if(data=='\n' || data=='\r' || len>=127) {
if (args.comment_char && !continuation){
fprintf(args.grbl_out_file,"%c ",args.comment_char);
}
buf[len]=0;
fprintf(args.grbl_out_file,"%s",buf);
continuation = (len>=128); //print comment on next line unless we are only printing to avoid buffer overflow)
len=0;
}
}

View File

@ -25,30 +25,56 @@
#include <stdio.h>
#include "../nuts_bolts.h"
#include "../system.h"
#include "platform.h"
// Output file handles
extern FILE *block_out_file;
extern FILE *step_out_file;
//simulation globals
typedef struct sim_vars {
uint64_t masterclock;
double sim_time; //current time of the simulation
uint8_t started; //don't start timers until first char recieved.
uint8_t exit;
float speedup;
int32_t baud_ticks;
double next_print_time;
// This variable is needed to determine if execute_runtime() is called in a loop
// waiting for the buffer to empty, as in plan_synchronize()
extern int runtime_second_call;
} sim_vars_t;
extern sim_vars_t sim;
typedef struct arg_vars {
// Output file handles
FILE *block_out_file;
FILE *step_out_file;
FILE *grbl_out_file;
// Minimum time step for printing stepper values. //Given by user via command line
double step_time;
//char to prefix comments; default '#'
uint8_t comment_char;
} arg_vars_t;
extern arg_vars_t args;
// Minimum time step for printing stepper values. Given by user via command line
extern double step_time;
// global system variable structure for position etc.
extern system_t sys;
extern uint8_t print_comment;
// setup avr simulation
void init_simulator(float time_multiplier);
//shutdown simulator - close open files
int shutdown_simulator(uint8_t exitflag);
//simulates the hardware until sim.exit is set.
void sim_loop();
// Call the stepper interrupt until one block is finished
void sim_stepper();
// Check if buffer is full or if plan_synchronize() wants to clear the buffer
void handle_buffer();
void simulate_serial();
// Print information about the most recently inserted block
void printBlock();
//printer for grbl serial port output
void grbl_out(uint8_t char_out);
#endif

View File

@ -2,8 +2,8 @@
spindle_control.c - spindle control methods
Part of Grbl
Copyright (c) 2012-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,39 +19,78 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "settings.h"
#include "system.h"
#include "spindle_control.h"
#include "planner.h"
#include "protocol.h"
#include "gcode.h"
static uint8_t current_direction;
void spindle_init()
{
current_direction = 0;
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT);
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT);
{
// On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
#ifdef VARIABLE_SPINDLE
SPINDLE_PWM_DDR |= (1<<SPINDLE_PWM_BIT); // Configure as PWM output pin.
#ifndef CPU_MAP_ATMEGA328P
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
#endif
#else
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
#endif
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
spindle_stop();
}
void spindle_stop()
{
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
// On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
#ifdef VARIABLE_SPINDLE
TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero.
#ifndef CPU_MAP_ATMEGA328P
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low.
#endif
#else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low.
#endif
}
void spindle_run(int8_t direction) //, uint16_t rpm)
void spindle_run(uint8_t direction, float rpm)
{
if (direction != current_direction) {
plan_synchronize();
if (direction) {
if(direction > 0) {
SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT);
} else {
SPINDLE_DIRECTION_PORT |= 1<<SPINDLE_DIRECTION_BIT;
}
SPINDLE_ENABLE_PORT |= 1<<SPINDLE_ENABLE_BIT;
if (sys.state == STATE_CHECK_MODE) { return; }
// Empty planner buffer to ensure spindle is set when programmed.
protocol_auto_cycle_start(); //temp fix for M3 lockup
protocol_buffer_synchronize();
// Halt or set spindle direction and rpm.
if (direction == SPINDLE_DISABLE) {
spindle_stop();
} else {
if (direction == SPINDLE_ENABLE_CW) {
SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT);
} else {
spindle_stop();
SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT);
}
current_direction = direction;
#ifdef VARIABLE_SPINDLE
#define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler
rpm -= SPINDLE_MIN_RPM;
if ( rpm > 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;
#ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared.
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
}
}

View File

@ -2,8 +2,8 @@
spindle_control.h - spindle control methods
Part of Grbl
Copyright (c) 2012-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2012 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,10 +22,14 @@
#ifndef spindle_control_h
#define spindle_control_h
#include <avr/io.h>
// Initializes spindle pins and hardware PWM, if enabled.
void spindle_init();
void spindle_run(int8_t direction); //, uint16_t rpm);
// Sets spindle direction and spindle rpm via PWM, if enabled.
void spindle_run(uint8_t direction, float rpm);
// Kills spindle.
void spindle_stop();
#endif

1057
stepper.c

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,10 +22,12 @@
#ifndef stepper_h
#define stepper_h
#include <avr/io.h>
#ifndef SEGMENT_BUFFER_SIZE
#define SEGMENT_BUFFER_SIZE 6
#endif
// Initialize and setup the stepper motor subsystem
void st_init();
void stepper_init();
// Enable steppers, but cycle does not start unless called by motion control or runtime command.
void st_wake_up();
@ -36,13 +38,15 @@ void st_go_idle();
// Reset the stepper subsystem variables
void st_reset();
// Notify the stepper subsystem to start executing the g-code program in buffer.
void st_cycle_start();
// Reloads step segment buffer. Called continuously by runtime execution system.
void st_prep_buffer();
// Reinitializes the buffer after a feed hold for a resume.
void st_cycle_reinitialize();
// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters();
// Initiates a feed hold of the running program
void st_feed_hold();
// Called by runtime status reporting if realtime rate reporting is enabled in config.h.
#ifdef REPORT_REALTIME_RATE
float st_get_realtime_rate();
#endif
#endif

201
system.c Normal file
View File

@ -0,0 +1,201 @@
/*
system.c - Handles system level commands and real-time processes
Part of Grbl
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 <http://www.gnu.org/licenses/>.
*/
#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 '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 '$' : // Prints Grbl settings [IDLE/ALARM]
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
else { report_grbl_settings(); }
break;
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 ) {
if (!(settings_read_build_info(line))) {
report_status_message(STATUS_SETTING_READ_FAIL);
} else {
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, &parameter)) { 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(parameter, value));
}
}
}
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
}

98
system.h Normal file
View File

@ -0,0 +1,98 @@
/*
system.h - Header for system level commands and real-time processes
Part of Grbl
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 <http://www.gnu.org/licenses/>.
*/
#ifndef system_h
#define system_h
// Define system header files and standard libraries used by Grbl
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <math.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// 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.
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the runtime protocol only needs to check for a non-zero value to
// know when there is a runtime command to execute.
#define 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 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 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.
} system_t;
extern system_t sys;
// Initialize the serial protocol
void system_init();
// 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);
#endif

View File

@ -0,0 +1,773 @@
(ShapeOko2 "Hello World" test file)
(Set up sheet of paper landscape, long edge on X-axis)
(Starting location around the lower[-y] left[-x] corner)
(originally Generated by PartKam Version 0.05)
G20 G90 (remove G40)
(write the words)
G0 Z0.125
(remove x)
G17
G0 X0.435 Y0.4042
G1 Z-0.01 F10
G3 X0.4557 Y0.4046 I0 J0.4533 F40
G3 X0.4728 Y0.4059 I-0.0142 J0.3117
G3 X0.4898 Y0.4082 I-0.0257 J0.2542
G3 X0.5034 Y0.4111 I-0.028 J0.1653
G3 X0.5168 Y0.415 I-0.0472 J0.1844
G3 X0.5278 Y0.4192 I-0.0435 J0.1296
G3 X0.5383 Y0.4245 I-0.0462 J0.1062
G3 X0.5468 Y0.4299 I-0.0415 J0.0739
G3 X0.5545 Y0.4363 I-0.0474 J0.0649
G3 X0.5606 Y0.4429 I-0.0441 J0.0468
G3 X0.5656 Y0.4503 I-0.0464 J0.0371
G3 X0.5694 Y0.4582 I-0.0481 J0.0278
G3 X0.5721 Y0.4664 I-0.0805 J0.031
G3 X0.5741 Y0.4754 I-0.093 J0.0254
G3 X0.5753 Y0.4846 I-0.1022 J0.0176
G3 X0.5757 Y0.4946 I-0.1224 J0.01
G3 X0.5731 Y0.5153 I-0.083 J0
G3 X0.5656 Y0.5337 I-0.0731 J-0.0188
G3 X0.5537 Y0.5498 I-0.072 J-0.0408
G3 X0.5355 Y0.5656 I-0.0876 J-0.083
G3 X0.5146 Y0.5784 I-0.1178 J-0.1676
G3 X0.4808 Y0.5949 I-0.2293 J-0.4268
G3 X0.4459 Y0.6092 I-0.282 J-0.6397
G3 X0.3973 Y0.6265 I-0.4624 J-1.2235
G2 X0.376 Y0.6343 I0.3353 J0.9488
G2 X0.3546 Y0.6427 I0.3684 J0.9724
G2 X0.3333 Y0.6515 I0.3967 J0.9808
G2 X0.3119 Y0.661 I0.4333 J1.0077
G2 X0.2909 Y0.6715 I0.1396 J0.3066
G2 X0.2714 Y0.683 I0.1507 J0.2779
G2 X0.2527 Y0.6959 I0.1666 J0.2606
G2 X0.2353 Y0.71 I0.1811 J0.2422
G2 X0.2191 Y0.7255 I0.1487 J0.1708
G2 X0.2045 Y0.7427 I0.1668 J0.1572
G2 X0.1916 Y0.7611 I0.1861 J0.1436
G2 X0.18 Y0.7816 I0.2168 J0.1363
G2 X0.1709 Y0.8032 I0.1508 J0.0765
G2 X0.164 Y0.8282 I0.1965 J0.0675
G2 X0.1601 Y0.854 I0.2316 J0.0483
G2 X0.1587 Y0.884 I0.3133 J0.03
G2 X0.1601 Y0.914 I0.3058 J0
G2 X0.1643 Y0.9413 I0.2576 J-0.0254
G2 X0.1715 Y0.968 I0.2377 J-0.0494
G2 X0.1813 Y0.9926 I0.2132 J-0.0704
G2 X0.1938 Y1.016 I0.2171 J-0.1015
G2 X0.2086 Y1.0374 I0.1951 J-0.1191
G2 X0.2257 Y1.0569 I0.1807 J-0.1407
G2 X0.2453 Y1.0749 I0.1727 J-0.1691
G2 X0.2668 Y1.0906 I0.1588 J-0.1942
G2 X0.2912 Y1.1049 I0.1534 J-0.2346
G2 X0.3168 Y1.1167 I0.1391 J-0.2681
G2 X0.3458 Y1.127 I0.1352 J-0.3335
G2 X0.3756 Y1.1348 I0.1068 J-0.3459
G2 X0.4086 Y1.1407 I0.0911 J-0.4184
G2 X0.442 Y1.144 I0.0638 J-0.4657
G2 X0.4789 Y1.1452 I0.037 J-0.5706
G2 X0.5228 Y1.144 I0 J-0.7643
G2 X0.5609 Y1.1405 I-0.0333 J-0.5787
G2 X0.5986 Y1.1344 I-0.062 J-0.5001
G2 X0.6309 Y1.1264 I-0.0758 J-0.3755
G2 X0.6627 Y1.1163 I-0.2335 J-0.7931
G2 X0.6893 Y1.1066 I-0.191 J-0.5638
G2 X0.7154 Y1.0955 I-0.1827 J-0.4653
G2 X0.7364 Y1.0849 I-0.1447 J-0.3148
G1 X0.6799 Y0.9304
G3 X0.6615 Y0.9394 I-0.2077 J-0.4011
G3 X0.6414 Y0.9482 I-0.2158 J-0.4675
G3 X0.621 Y0.9561 I-0.2093 J-0.5093
G3 X0.5989 Y0.9637 I-0.2164 J-0.5924
G3 X0.5764 Y0.9698 I-0.0747 J-0.2309
G3 X0.5498 Y0.9745 I-0.0698 J-0.3171
G3 X0.5228 Y0.9772 I-0.0502 J-0.3738
G3 X0.4915 Y0.9782 I-0.0314 J-0.5063
G3 X0.4565 Y0.9767 I0 J-0.4369
G3 X0.4311 Y0.9733 I0.0187 J-0.2329
G3 X0.4065 Y0.9666 I0.0312 J-0.1619
G3 X0.3904 Y0.9587 I0.0277 J-0.0771
G3 X0.3767 Y0.9474 I0.0374 J-0.0591
G3 X0.3673 Y0.9341 I0.0432 J-0.0405
G3 X0.3618 Y0.9186 I0.0563 J-0.029
G3 X0.3596 Y0.899 I0.0892 J-0.0196
G3 X0.3604 Y0.8872 I0.093 J0
G3 X0.3625 Y0.8771 I0.0704 J0.009
G3 X0.366 Y0.8674 I0.0617 J0.0173
G3 X0.3709 Y0.8588 I0.0525 J0.0244
G3 X0.377 Y0.851 I0.0702 J0.0485
G3 X0.3846 Y0.8433 I0.0772 J0.0678
G3 X0.3929 Y0.8363 I0.0778 J0.0845
G3 X0.403 Y0.8293 I0.0886 J0.1165
G3 X0.4135 Y0.823 I0.1229 J0.1933
G3 X0.4253 Y0.8168 I0.1326 J0.2355
G3 X0.4373 Y0.8111 I0.1314 J0.2629
G3 X0.4507 Y0.8055 I0.1413 J0.3181
G3 X0.4642 Y0.8002 I0.4349 J1.1
G3 X0.4791 Y0.7946 I0.5053 J1.3254
G3 X0.4941 Y0.7892 I0.5351 J1.4522
G3 X0.5104 Y0.7835 I0.615 J1.7234
G2 X0.5441 Y0.7706 I-0.62 J-1.6745
G2 X0.5738 Y0.7585 I-0.5126 J-1.3027
G2 X0.6031 Y0.7457 I-0.4768 J-1.135
G2 X0.6284 Y0.7339 I-0.3858 J-0.8553
G2 X0.653 Y0.7208 I-0.1756 J-0.3603
G2 X0.6744 Y0.7073 I-0.164 J-0.2844
G2 X0.6946 Y0.6922 I-0.1689 J-0.2468
G2 X0.7119 Y0.6767 I-0.1605 J-0.1966
G2 X0.7276 Y0.6596 I-0.1493 J-0.1521
G2 X0.741 Y0.6412 I-0.1581 J-0.1293
G2 X0.7522 Y0.6215 I-0.172 J-0.1113
G2 X0.7615 Y0.6001 I-0.1936 J-0.0968
G2 X0.7685 Y0.5778 I-0.2008 J-0.0746
G2 X0.7738 Y0.5514 I-0.2755 J-0.0693
G2 X0.7768 Y0.5247 I-0.3264 J-0.0499
G2 X0.7779 Y0.4934 I-0.4455 J-0.0313
G2 X0.772 Y0.4337 I-0.3082 J0
G2 X0.7565 Y0.3852 I-0.2183 J0.0431
G2 X0.7301 Y0.3418 I-0.1887 J0.0852
G2 X0.6925 Y0.3043 I-0.1784 J0.1413
G2 X0.647 Y0.2762 I-0.146 J0.1856
G2 X0.5854 Y0.2539 I-0.1514 J0.3216
G2 X0.5204 Y0.2419 I-0.1159 J0.4443
G2 X0.435 Y0.2371 I-0.0854 J0.7648
G2 X0.4061 Y0.2376 I0 J0.919
G2 X0.38 Y0.2388 I0.0236 J0.75
G2 X0.354 Y0.2411 I0.0448 J0.6746
G2 X0.3307 Y0.244 I0.0572 J0.5433
G2 X0.3075 Y0.2478 I0.1319 J0.886
G2 X0.2869 Y0.2517 I0.1238 J0.7045
G2 X0.2664 Y0.2563 I0.1284 J0.6229
G2 X0.2485 Y0.261 I0.1166 J0.4841
G2 X0.2307 Y0.2663 I0.1781 J0.6347
G2 X0.2152 Y0.2714 I0.1505 J0.4834
G2 X0.1999 Y0.277 I0.1443 J0.4161
G2 X0.1869 Y0.2823 I0.1191 J0.3062
G2 X0.1741 Y0.2881 I0.2781 J0.6336
G2 X0.1632 Y0.2933 I0.2146 J0.4632
G2 X0.1524 Y0.2988 I0.1915 J0.3889
G2 X0.1436 Y0.3037 I0.1422 J0.2695
G1 X0.1988 Y0.4594
G3 X0.2186 Y0.4494 I0.1622 J0.2959
G3 X0.2423 Y0.4392 I0.1933 J0.4152
G3 X0.2666 Y0.4302 I0.1967 J0.4934
G3 X0.2949 Y0.4211 I0.2292 J0.6675
G3 X0.3238 Y0.414 I0.0929 J0.313
G3 X0.3585 Y0.4084 I0.0897 J0.4486
G3 X0.3936 Y0.4053 I0.065 J0.537
G3 X0.435 Y0.4042 I0.0414 J0.7464
G0 Z0.125
G0 X1.4536 Y1.1251
G1 Z-0.01 F10
G1 X1.6496 Y1.1251 F40
G1 X1.6496 Y0.2547
G1 X1.4536 Y0.2547
G1 X1.4536 Y0.624
G1 X1.1246 Y0.624
G1 X1.1246 Y0.2547
G1 X0.9286 Y0.2547
G1 X0.9286 Y1.1251
G1 X1.1246 Y1.1251
G1 X1.1246 Y0.7923
G1 X1.4536 Y0.7923
G1 X1.4536 Y1.1251
G0 Z0.125
G0 X2.4338 Y0.2547
G1 Z-0.01 F10
G1 X2.403 Y0.3477 F40
G1 X2.3698 Y0.4431
G1 X2.0306 Y0.4431
G1 X1.9973 Y0.3477
G1 X1.9666 Y0.2547
G1 X1.7631 Y0.2547
G2 X1.7877 Y0.325 I17.8599 J-6.2193
G2 X1.8108 Y0.39 I15.3154 J-5.401
G2 X1.8342 Y0.455 I14.1288 J-5.0502
G2 X1.856 Y0.5147 I11.9637 J-4.3385
G2 X1.8782 Y0.5743 I9.7422 J-3.588
G2 X1.8995 Y0.6306 I8.7097 J-3.2684
G2 X1.9212 Y0.6867 I8.2125 J-3.1426
G2 X1.9421 Y0.7395 I7.2956 J-2.8491
G2 X1.9633 Y0.7922 I8.4224 J-3.3598
G2 X1.9838 Y0.8424 I7.6529 J-3.1085
G2 X2.0047 Y0.8924 I7.277 J-3.0116
G2 X2.025 Y0.9399 I6.5821 J-2.7772
G2 X2.0457 Y0.9872 I4.2002 J-1.8081
G2 X2.0666 Y1.0336 I4.0692 J-1.8064
G2 X2.0881 Y1.0798 I3.9952 J-1.8286
G2 X2.1098 Y1.1251 I3.8717 J-1.8266
G1 X2.2969 Y1.1251
G2 X2.3181 Y1.0798 I-6.0675 J-2.8652
G2 X2.3393 Y1.0336 I-6.3027 J-2.919
G2 X2.3602 Y0.9872 I-6.4339 J-2.9227
G2 X2.381 Y0.9399 I-6.6806 J-2.977
G2 X2.4016 Y0.8924 I-5.4185 J-2.3686
G2 X2.4227 Y0.8424 I-5.9798 J-2.5519
G2 X2.4433 Y0.7922 I-6.2861 J-2.6208
G2 X2.4646 Y0.7395 I-6.9077 J-2.8155
G2 X2.4854 Y0.6867 I-7.3628 J-2.9356
G2 X2.5071 Y0.6306 I-8.2907 J-3.237
G2 X2.5284 Y0.5743 I-8.7947 J-3.3654
G2 X2.5506 Y0.5147 I-9.8397 J-3.6933
G2 X2.5724 Y0.455 I-11.8601 J-4.3698
G2 X2.5958 Y0.39 I-14.0105 J-5.0822
G2 X2.6189 Y0.325 I-15.1905 J-5.4307
G2 X2.6436 Y0.2547 I-17.7191 J-6.2494
G1 X2.4338 Y0.2547
G0 Z0.125
G0 X2.1989 Y0.9279
G1 Z-0.01 F10
G2 X2.1957 Y0.9185 I-0.5641 J0.1881 F40
G2 X2.1911 Y0.9056 I-1.0677 J0.3759
G2 X2.1863 Y0.8928 I-1.4028 J0.513
G2 X2.1801 Y0.8764 I-2.2751 J0.8558
G3 X2.1738 Y0.8601 I30.9994 J-11.916
G3 X2.1666 Y0.8413 I41.2401 J-15.8275
G3 X2.1594 Y0.8224 I47.1201 J-18.0595
G3 X2.1512 Y0.8011 I60.4881 J-23.1553
G3 X2.143 Y0.7797 I19.8859 J-7.6046
G3 X2.1341 Y0.7562 I24.1414 J-9.2022
G3 X2.1251 Y0.7326 I26.4832 J-10.0653
G3 X2.1154 Y0.7069 I31.5951 J-11.976
G3 X2.1057 Y0.6811 I4.794 J-1.8127
G3 X2.0958 Y0.6541 I5.244 J-1.9507
G3 X2.086 Y0.6271 I5.4846 J-2.0082
G3 X2.0758 Y0.5988 I5.9771 J-2.1552
G1 X2.3233 Y0.5988
G3 X2.3132 Y0.6271 I-12.9639 J-4.6057
G3 X2.3035 Y0.6541 I-11.8652 J-4.2445
G3 X2.2937 Y0.6811 I-11.3354 J-4.0841
G3 X2.2843 Y0.7069 I-10.3324 J-3.7506
G3 X2.2748 Y0.7326 I-4.3139 J-1.5783
G3 X2.266 Y0.7562 I-3.6331 J-1.3538
G3 X2.2569 Y0.7797 I-3.3175 J-1.2608
G3 X2.2485 Y0.8011 I-2.7503 J-1.0677
G2 X2.2401 Y0.8224 I9.4214 J3.7426
G2 X2.2327 Y0.8413 I7.3257 J2.8909
G2 X2.2253 Y0.8601 I6.4077 J2.5096
G2 X2.219 Y0.8764 I4.802 J1.8645
G3 X2.2127 Y0.8927 I-7.2811 J-2.7989
G3 X2.2077 Y0.9056 I-4.551 J-1.7612
G3 X2.2027 Y0.9185 I-3.4837 J-1.3595
G3 X2.1989 Y0.9279 I-1.8801 J-0.7417
G0 Z0.125
G0 X3.0175 Y1.1352
G1 Z-0.01 F10
G2 X3.1143 Y1.1304 I0 J-0.9783 F40
G2 X3.1895 Y1.118 I-0.06 J-0.603
G2 X3.2619 Y1.0953 I-0.1063 J-0.4642
G2 X3.3164 Y1.0667 I-0.1178 J-0.2911
G2 X3.362 Y1.0268 I-0.1208 J-0.184
G2 X3.3946 Y0.9764 I-0.1634 J-0.1415
G2 X3.4133 Y0.9188 I-0.2268 J-0.1055
G2 X3.4207 Y0.8425 I-0.3919 J-0.0763
G2 X3.4132 Y0.7656 I-0.4013 J0
G2 X3.3943 Y0.7073 I-0.2484 J0.0485
G2 X3.3613 Y0.6563 I-0.1992 J0.0927
G2 X3.3152 Y0.6158 I-0.1701 J0.147
G2 X3.26 Y0.5867 I-0.1755 J0.2656
G2 X3.187 Y0.5635 I-0.1806 J0.4426
G2 X3.1111 Y0.551 I-0.1365 J0.5905
G2 X3.0137 Y0.5461 I-0.0974 J0.969
G1 X2.9522 Y0.5461
G1 X2.9522 Y0.2547
G1 X2.7562 Y0.2547
G1 X2.7562 Y1.1126
G2 X2.7883 Y1.1183 I0.19 J-0.9718
G2 X2.8222 Y1.1232 I0.1732 J-1.0725
G2 X2.8561 Y1.1271 I0.1462 J-1.1296
G2 X2.8919 Y1.1301 I0.1235 J-1.2476
G2 X2.9277 Y1.1324 I0.1789 J-2.5504
G2 X2.9591 Y1.1339 I0.1097 J-1.9581
G2 X2.9905 Y1.1349 I0.068 J-1.701
G2 X3.0175 Y1.1352 I0.027 J-1.257
G0 Z0.125
G0 X3.03 Y0.9681
G1 Z-0.01 F10
G1 X2.988 Y0.9669 F40
G3 X2.9775 Y0.9662 I0.1375 J-2.2842
G3 X2.9686 Y0.9656 I0.1084 J-1.6742
G3 X2.9597 Y0.965 I0.0994 J-1.4177
G3 X2.9522 Y0.9644 I0.0758 J-0.9911
G1 X2.9522 Y0.7131
G1 X3.0137 Y0.7131
G3 X3.0644 Y0.7151 I0 J0.6562
G3 X3.1029 Y0.72 I-0.0297 J0.3827
G3 X3.1403 Y0.7294 I-0.051 J0.2826
G3 X3.1669 Y0.7408 I-0.0497 J0.1535
G3 X3.1895 Y0.7577 I-0.0439 J0.0818
G3 X3.2056 Y0.7803 I-0.0644 J0.0629
G3 X3.2147 Y0.807 I-0.0959 J0.048
G3 X3.2184 Y0.8438 I-0.1813 J0.0368
G3 X3.2175 Y0.8619 I-0.1838 J0
G3 X3.2151 Y0.877 I-0.1312 J-0.013
G3 X3.2109 Y0.8918 I-0.1104 J-0.0241
G3 X3.2052 Y0.9041 I-0.0828 J-0.0303
G3 X3.1979 Y0.9154 I-0.0877 J-0.0485
G3 X3.1892 Y0.9256 I-0.078 J-0.058
G3 X3.1793 Y0.9344 I-0.0709 J-0.0697
G3 X3.1676 Y0.9424 I-0.0682 J-0.088
G3 X3.155 Y0.9489 I-0.0659 J-0.1118
G3 X3.1404 Y0.9546 I-0.0654 J-0.1451
G3 X3.1253 Y0.959 I-0.0571 J-0.1695
G3 X3.1079 Y0.9625 I-0.0538 J-0.2228
G3 X3.0903 Y0.9649 I-0.0622 J-0.3868
G3 X3.0709 Y0.9667 I-0.0535 J-0.4675
G3 X3.0514 Y0.9677 I-0.0373 J-0.514
G3 X3.03 Y0.9681 I-0.0213 J-0.6163
G0 Z0.125
G0 X3.5657 Y0.2547
G1 Z-0.01 F10
G1 X3.5657 Y1.1251 F40
G1 X4.1536 Y1.1251
G1 X4.1536 Y0.9606
G1 X3.7617 Y0.9606
G1 X3.7617 Y0.7898
G1 X4.1096 Y0.7898
G1 X4.1096 Y0.629
G1 X3.7617 Y0.629
G1 X3.7617 Y0.4193
G1 X4.1825 Y0.4193
G1 X4.1825 Y0.2547
G1 X3.5657 Y0.2547
G0 Z0.125
G0 X4.4908 Y0.6905
G1 Z-0.01 F10
G3 X4.4918 Y0.6586 I0.4971 J0 F40
G3 X4.4947 Y0.6296 I0.4109 J0.0265
G3 X4.4997 Y0.601 I0.3733 J0.0507
G3 X4.5065 Y0.575 I0.3139 J0.0676
G3 X4.5154 Y0.5497 I0.3036 J0.0925
G3 X4.5256 Y0.5271 I0.2511 J0.1004
G3 X4.5379 Y0.5056 I0.2254 J0.1151
G3 X4.5517 Y0.4864 I0.1934 J0.124
G3 X4.5674 Y0.4689 I0.1636 J0.1303
G3 X4.5845 Y0.4536 I0.1398 J0.1398
G3 X4.6033 Y0.4405 I0.1224 J0.1548
G3 X4.6239 Y0.4293 I0.109 J0.1765
G3 X4.6457 Y0.4206 I0.0819 J0.174
G3 X4.6698 Y0.4142 I0.0674 J0.205
G3 X4.6944 Y0.4105 I0.0471 J0.2296
G3 X4.7219 Y0.4092 I0.0275 J0.2849
G3 X4.7487 Y0.4105 I0 J0.2758
G3 X4.7732 Y0.4142 I-0.0227 J0.2323
G3 X4.7971 Y0.4206 I-0.0443 J0.2143
G3 X4.8192 Y0.4293 I-0.0629 J0.192
G3 X4.8402 Y0.4405 I-0.0881 J0.1899
G3 X4.8591 Y0.4536 I-0.1024 J0.1678
G3 X4.8763 Y0.4689 I-0.1208 J0.154
G3 X4.8921 Y0.4864 I-0.1445 J0.1452
G3 X4.9058 Y0.5056 I-0.1798 J0.1433
G3 X4.9181 Y0.5271 I-0.2133 J0.1367
G3 X4.9284 Y0.5497 I-0.2412 J0.1232
G3 X4.9373 Y0.575 I-0.295 J0.1181
G3 X4.944 Y0.601 I-0.3069 J0.0937
G3 X4.9491 Y0.6296 I-0.3679 J0.0794
G3 X4.952 Y0.6586 I-0.4076 J0.0554
G3 X4.953 Y0.6905 I-0.4955 J0.032
G3 X4.952 Y0.7225 I-0.4983 J0
G3 X4.9491 Y0.7516 I-0.4161 J-0.0268
G3 X4.944 Y0.7804 I-0.3801 J-0.0514
G3 X4.9373 Y0.8067 I-0.3232 J-0.0691
G3 X4.9284 Y0.8324 I-0.3076 J-0.0928
G3 X4.9181 Y0.8551 I-0.252 J-0.0999
G3 X4.9058 Y0.8767 I-0.2249 J-0.1141
G3 X4.8921 Y0.8959 I-0.1909 J-0.1218
G3 X4.8763 Y0.9135 I-0.1602 J-0.1276
G3 X4.8591 Y0.9287 I-0.1379 J-0.1385
G3 X4.8402 Y0.9418 I-0.1212 J-0.1544
G3 X4.8192 Y0.9531 I-0.1088 J-0.1784
G3 X4.7971 Y0.9618 I-0.085 J-0.1835
G3 X4.7732 Y0.9681 I-0.0682 J-0.2081
G3 X4.7487 Y0.9718 I-0.0472 J-0.2288
G3 X4.7219 Y0.9731 I-0.0269 J-0.2747
G3 X4.6944 Y0.9718 I0 J-0.2778
G3 X4.6698 Y0.968 I0.0226 J-0.2269
G3 X4.6458 Y0.9614 I0.0436 J-0.206
G3 X4.6239 Y0.9524 I0.0608 J-0.1793
G3 X4.6033 Y0.941 I0.0938 J-0.1933
G3 X4.5845 Y0.9276 I0.1085 J-0.1725
G3 X4.5674 Y0.9123 I0.1271 J-0.159
G3 X4.5517 Y0.8947 I0.1514 J-0.1507
G3 X4.538 Y0.8755 I0.1766 J-0.1407
G3 X4.5256 Y0.8538 I0.2121 J-0.1354
G3 X4.5154 Y0.8311 I0.2413 J-0.1223
G3 X4.5065 Y0.8055 I0.2984 J-0.1182
G3 X4.4997 Y0.7792 I0.3216 J-0.0968
G3 X4.4947 Y0.7507 I0.3729 J-0.0801
G3 X4.4918 Y0.7219 I0.4066 J-0.0555
G3 X4.4908 Y0.6905 I0.4804 J-0.0313
G0 Z0.125
G0 X5.1552 Y0.6905
G1 Z-0.01 F10
G2 X5.153 Y0.6348 I-0.7056 J0 F40
G2 X5.1469 Y0.5855 I-0.5592 J0.0443
G2 X5.1362 Y0.537 I-0.4973 J0.0842
G2 X5.1219 Y0.494 I-0.4061 J0.1107
G2 X5.1034 Y0.4526 I-0.4475 J0.1756
G2 X5.0825 Y0.4161 I-0.3674 J0.1859
G2 X5.058 Y0.3821 I-0.3264 J0.2094
G2 X5.0309 Y0.3521 I-0.2803 J0.2261
G2 X5.0006 Y0.3252 I-0.2579 J0.2609
G2 X4.9676 Y0.302 I-0.2269 J0.2867
G2 X4.9322 Y0.2824 I-0.1983 J0.3171
G2 X4.8933 Y0.266 I-0.1783 J0.3683
G2 X4.8529 Y0.2534 I-0.1576 J0.4351
G2 X4.8106 Y0.2443 I-0.1231 J0.4701
G2 X4.7676 Y0.239 I-0.0841 J0.4988
G2 X4.7219 Y0.2371 I-0.0457 J0.5609
G2 X4.6774 Y0.239 I0 J0.5346
G2 X4.6352 Y0.2443 I0.0407 J0.4884
G2 X4.5936 Y0.2534 I0.0806 J0.4691
G2 X4.5536 Y0.266 I0.1189 J0.4479
G2 X4.5149 Y0.2824 I0.1418 J0.3881
G2 X4.4795 Y0.302 I0.1683 J0.3468
G2 X4.4463 Y0.3252 I0.2011 J0.3229
G2 X4.4154 Y0.3521 I0.2387 J0.3054
G2 X4.3877 Y0.3821 I0.2524 J0.2608
G2 X4.3627 Y0.4161 I0.2987 J0.246
G2 X4.3414 Y0.4526 I0.3422 J0.2241
G2 X4.3225 Y0.494 I0.4215 J0.2176
G2 X4.3079 Y0.537 I0.3858 J0.1543
G2 X4.297 Y0.5855 I0.4777 J0.1328
G2 X4.2908 Y0.6348 I0.5427 J0.0937
G2 X4.2885 Y0.6905 I0.6897 J0.0557
G2 X4.2909 Y0.7462 I0.6678 J0
G2 X4.2973 Y0.7956 I0.5306 J-0.0444
G2 X4.3086 Y0.844 I0.473 J-0.0846
G2 X4.3237 Y0.8871 I0.3899 J-0.1124
G2 X4.3432 Y0.9285 I0.4519 J-0.1874
G2 X4.365 Y0.9652 I0.376 J-0.199
G2 X4.3904 Y0.9993 I0.3361 J-0.2236
G2 X4.4185 Y1.0297 I0.2923 J-0.2425
G2 X4.4497 Y1.0569 I0.2779 J-0.287
G2 X4.4829 Y1.0802 I0.2379 J-0.303
G2 X4.5184 Y1.1 I0.2055 J-0.3276
G2 X4.5567 Y1.1163 I0.1789 J-0.3656
G2 X4.5964 Y1.129 I0.1604 J-0.4356
G2 X4.6372 Y1.138 I0.1212 J-0.4502
G2 X4.6787 Y1.1434 I0.0819 J-0.4678
G2 X4.7219 Y1.1452 I0.0432 J-0.5057
G2 X4.7663 Y1.1434 I0 J-0.535
G2 X4.8085 Y1.138 I-0.0407 J-0.4886
G2 X4.8501 Y1.1289 I-0.0806 J-0.4692
G2 X4.8902 Y1.1163 I-0.1189 J-0.4478
G2 X4.9288 Y1.0999 I-0.1394 J-0.3816
G2 X4.9643 Y1.0802 I-0.1667 J-0.3421
G2 X4.9975 Y1.0568 I-0.2001 J-0.3191
G2 X5.0284 Y1.0297 I-0.2393 J-0.3033
G2 X5.0561 Y0.9993 I-0.2595 J-0.2651
G2 X5.0811 Y0.9652 I-0.3049 J-0.2492
G2 X5.1024 Y0.9285 I-0.3477 J-0.2267
G2 X5.1213 Y0.8871 I-0.4252 J-0.219
G2 X5.1358 Y0.844 I-0.3859 J-0.1543
G2 X5.1467 Y0.7956 I-0.4781 J-0.1328
G2 X5.1529 Y0.7462 I-0.5432 J-0.0937
G2 X5.1552 Y0.6905 I-0.6906 J-0.0557
G0 Z0.125
G0 X5.831 Y0.2547
G1 Z-0.01 F10
G3 X5.8176 Y0.2759 I-1.044 J-0.6457 F40
G3 X5.8023 Y0.2991 I-1.2661 J-0.8193
G3 X5.7865 Y0.3221 I-1.3802 J-0.9298
G3 X5.7688 Y0.347 I-1.648 J-1.1507
G3 X5.7507 Y0.3716 I-1.2024 J-0.8671
G3 X5.7315 Y0.3966 I-1.2587 J-0.9478
G3 X5.7118 Y0.4213 I-1.2783 J-1.0032
G3 X5.691 Y0.4462 I-1.3393 J-1.0938
G3 X5.6697 Y0.4708 I-1.3401 J-1.1372
G3 X5.6481 Y0.4949 I-1.3041 J-1.1488
G3 X5.6261 Y0.5186 I-1.2762 J-1.1671
G3 X5.6037 Y0.5417 I-1.2437 J-1.1807
G3 X5.5807 Y0.5643 I-0.7609 J-0.7501
G3 X5.5585 Y0.5849 I-0.6558 J-0.6866
G3 X5.5355 Y0.6048 I-0.5973 J-0.6668
G3 X5.5132 Y0.6227 I-0.5113 J-0.6113
G1 X5.5132 Y0.2547
G1 X5.3173 Y0.2547
G1 X5.3173 Y1.1251
G1 X5.5132 Y1.1251
G1 X5.5132 Y0.796
G3 X5.5514 Y0.8364 I-4.1871 J3.9914
G3 X5.59 Y0.8782 I-4.4359 J4.1481
G3 X5.6283 Y0.9203 I-4.5842 J4.207
G3 X5.6671 Y0.9637 I-4.8507 J4.3705
G2 X5.7058 Y1.0073 I26.9704 J-23.868
G2 X5.7417 Y1.0477 I23.1463 J-20.5506
G2 X5.7777 Y1.088 I21.3614 J-19.0327
G2 X5.8109 Y1.1251 I18.1034 J-16.1914
G1 X6.0433 Y1.1251
G2 X5.9984 Y1.0726 I-5.3969 J4.5615
G2 X5.9539 Y1.0215 I-5.1497 J4.4395
G2 X5.9089 Y0.9709 I-5.0078 J4.405
G2 X5.8643 Y0.9217 I-4.7755 J4.2874
G2 X5.8191 Y0.8729 I-4.3297 J3.9689
G2 X5.7721 Y0.8234 I-4.526 J4.244
G2 X5.7246 Y0.7743 I-4.6029 J4.4123
G2 X5.6753 Y0.7244 I-4.8095 J4.7102
G2 X5.7262 Y0.6798 I-0.8185 J-0.9847
G2 X5.7778 Y0.6296 I-0.9824 J-1.0622
G2 X5.8269 Y0.577 I-1.1027 J-1.0792
G2 X5.8769 Y0.5185 I-1.3152 J-1.1722
G2 X5.9244 Y0.458 I-1.6952 J-1.3829
G2 X5.9722 Y0.3929 I-1.9177 J-1.4551
G2 X6.0177 Y0.3262 I-2.064 J-1.4598
G2 X6.0634 Y0.2547 I-2.3302 J-1.5388
G1 X5.831 Y0.2547
G0 Z0.125
G0 X6.3404 Y0.6905
G1 Z-0.01 F10
G3 X6.3415 Y0.6586 I0.4971 J0 F40
G3 X6.3444 Y0.6296 I0.4109 J0.0265
G3 X6.3494 Y0.601 I0.3733 J0.0507
G3 X6.3561 Y0.575 I0.3139 J0.0676
G3 X6.365 Y0.5497 I0.3036 J0.0925
G3 X6.3753 Y0.5271 I0.2511 J0.1004
G3 X6.3876 Y0.5056 I0.2254 J0.1151
G3 X6.4013 Y0.4864 I0.1934 J0.124
G3 X6.417 Y0.4689 I0.1636 J0.1303
G3 X6.4342 Y0.4536 I0.1398 J0.1398
G3 X6.4529 Y0.4405 I0.1224 J0.1548
G3 X6.4736 Y0.4293 I0.109 J0.1765
G3 X6.4954 Y0.4206 I0.0819 J0.174
G3 X6.5194 Y0.4142 I0.0674 J0.205
G3 X6.5441 Y0.4105 I0.0471 J0.2296
G3 X6.5715 Y0.4092 I0.0275 J0.2849
G3 X6.5984 Y0.4105 I0 J0.2758
G3 X6.6229 Y0.4142 I-0.0227 J0.2323
G3 X6.6468 Y0.4206 I-0.0443 J0.2143
G3 X6.6689 Y0.4293 I-0.0629 J0.192
G3 X6.6898 Y0.4405 I-0.0881 J0.1899
G3 X6.7088 Y0.4536 I-0.1024 J0.1678
G3 X6.726 Y0.4689 I-0.1208 J0.154
G3 X6.7417 Y0.4864 I-0.1445 J0.1452
G3 X6.7555 Y0.5056 I-0.1798 J0.1433
G3 X6.7678 Y0.5271 I-0.2133 J0.1367
G3 X6.778 Y0.5497 I-0.2412 J0.1232
G3 X6.787 Y0.575 I-0.295 J0.1181
G3 X6.7937 Y0.601 I-0.3069 J0.0937
G3 X6.7987 Y0.6296 I-0.3679 J0.0794
G3 X6.8016 Y0.6586 I-0.4076 J0.0554
G3 X6.8027 Y0.6905 I-0.4955 J0.032
G3 X6.8016 Y0.7225 I-0.4983 J0
G3 X6.7987 Y0.7516 I-0.4161 J-0.0268
G3 X6.7937 Y0.7804 I-0.3801 J-0.0514
G3 X6.787 Y0.8067 I-0.3232 J-0.0691
G3 X6.778 Y0.8324 I-0.3076 J-0.0928
G3 X6.7678 Y0.8551 I-0.252 J-0.0999
G3 X6.7554 Y0.8767 I-0.2249 J-0.1141
G3 X6.7417 Y0.8959 I-0.1909 J-0.1218
G3 X6.726 Y0.9135 I-0.1602 J-0.1276
G3 X6.7088 Y0.9287 I-0.1379 J-0.1385
G3 X6.6898 Y0.9418 I-0.1212 J-0.1544
G3 X6.6689 Y0.9531 I-0.1088 J-0.1784
G3 X6.6468 Y0.9618 I-0.085 J-0.1835
G3 X6.6229 Y0.9681 I-0.0682 J-0.2081
G3 X6.5984 Y0.9718 I-0.0472 J-0.2288
G3 X6.5715 Y0.9731 I-0.0269 J-0.2747
G3 X6.5441 Y0.9718 I0 J-0.2778
G3 X6.5194 Y0.968 I0.0226 J-0.2269
G3 X6.4954 Y0.9614 I0.0436 J-0.206
G3 X6.4736 Y0.9524 I0.0608 J-0.1793
G3 X6.4529 Y0.941 I0.0938 J-0.1933
G3 X6.4342 Y0.9276 I0.1085 J-0.1725
G3 X6.417 Y0.9123 I0.1271 J-0.159
G3 X6.4013 Y0.8947 I0.1514 J-0.1507
G3 X6.3876 Y0.8755 I0.1766 J-0.1407
G3 X6.3753 Y0.8538 I0.2121 J-0.1354
G3 X6.3651 Y0.8311 I0.2413 J-0.1223
G3 X6.3561 Y0.8055 I0.2984 J-0.1182
G3 X6.3494 Y0.7792 I0.3216 J-0.0968
G3 X6.3444 Y0.7507 I0.3729 J-0.0801
G3 X6.3415 Y0.7219 I0.4066 J-0.0555
G3 X6.3404 Y0.6905 I0.4804 J-0.0313
G0 Z0.125
G0 X7.0049 Y0.6905
G1 Z-0.01 F10
G2 X7.0027 Y0.6348 I-0.7056 J0 F40
G2 X6.9965 Y0.5855 I-0.5592 J0.0443
G2 X6.9859 Y0.537 I-0.4973 J0.0842
G2 X6.9716 Y0.494 I-0.4061 J0.1107
G2 X6.953 Y0.4526 I-0.4475 J0.1756
G2 X6.9322 Y0.4161 I-0.3674 J0.1859
G2 X6.9077 Y0.3821 I-0.3264 J0.2094
G2 X6.8805 Y0.3521 I-0.2803 J0.2261
G2 X6.8503 Y0.3252 I-0.2579 J0.2609
G2 X6.8173 Y0.302 I-0.2269 J0.2867
G2 X6.7819 Y0.2824 I-0.1983 J0.3171
G2 X6.743 Y0.266 I-0.1783 J0.3683
G2 X6.7026 Y0.2534 I-0.1576 J0.4351
G2 X6.6602 Y0.2443 I-0.1231 J0.4701
G2 X6.6172 Y0.239 I-0.0841 J0.4988
G2 X6.5715 Y0.2371 I-0.0457 J0.5609
G2 X6.5271 Y0.239 I0 J0.5346
G2 X6.4849 Y0.2443 I0.0407 J0.4884
G2 X6.4433 Y0.2534 I0.0806 J0.4691
G2 X6.4032 Y0.266 I0.1189 J0.4479
G2 X6.3646 Y0.2824 I0.1418 J0.3881
G2 X6.3291 Y0.302 I0.1683 J0.3468
G2 X6.2959 Y0.3252 I0.2011 J0.3229
G2 X6.2651 Y0.3521 I0.2387 J0.3054
G2 X6.2374 Y0.3821 I0.2524 J0.2608
G2 X6.2123 Y0.4161 I0.2987 J0.246
G2 X6.191 Y0.4526 I0.3422 J0.2241
G2 X6.1721 Y0.494 I0.4215 J0.2176
G2 X6.1576 Y0.537 I0.3858 J0.1544
G2 X6.1467 Y0.5855 I0.4777 J0.1328
G2 X6.1405 Y0.6348 I0.5427 J0.0937
G2 X6.1382 Y0.6905 I0.6897 J0.0557
G2 X6.1405 Y0.7462 I0.6678 J0
G2 X6.147 Y0.7956 I0.5306 J-0.0444
G2 X6.1583 Y0.844 I0.473 J-0.0846
G2 X6.1734 Y0.8871 I0.3899 J-0.1124
G2 X6.1929 Y0.9285 I0.4519 J-0.1874
G2 X6.2147 Y0.9652 I0.376 J-0.199
G2 X6.2401 Y0.9993 I0.3361 J-0.2236
G2 X6.2682 Y1.0297 I0.2923 J-0.2425
G2 X6.2994 Y1.0569 I0.2779 J-0.287
G2 X6.3326 Y1.0802 I0.2379 J-0.303
G2 X6.3681 Y1.1 I0.2055 J-0.3276
G2 X6.4064 Y1.1163 I0.1789 J-0.3656
G2 X6.4461 Y1.129 I0.1604 J-0.4356
G2 X6.4869 Y1.138 I0.1212 J-0.4502
G2 X6.5284 Y1.1434 I0.0819 J-0.4678
G2 X6.5715 Y1.1452 I0.0432 J-0.5057
G2 X6.616 Y1.1434 I0 J-0.535
G2 X6.6582 Y1.138 I-0.0407 J-0.4886
G2 X6.6998 Y1.1289 I-0.0806 J-0.4692
G2 X6.7399 Y1.1163 I-0.1189 J-0.4478
G2 X6.7785 Y1.0999 I-0.1394 J-0.3816
G2 X6.814 Y1.0802 I-0.1667 J-0.3421
G2 X6.8471 Y1.0568 I-0.2001 J-0.3191
G2 X6.878 Y1.0297 I-0.2393 J-0.3033
G2 X6.9058 Y0.9993 I-0.2595 J-0.2651
G2 X6.9308 Y0.9652 I-0.3049 J-0.2492
G2 X6.9521 Y0.9285 I-0.3477 J-0.2267
G2 X6.971 Y0.8871 I-0.4252 J-0.219
G2 X6.9855 Y0.844 I-0.3859 J-0.1543
G2 X6.9964 Y0.7956 I-0.4781 J-0.1328
G2 X7.0026 Y0.7462 I-0.5432 J-0.0937
G2 X7.0049 Y0.6905 I-0.6906 J-0.0557
G0 Z0.125
G0 X7.9912 Y0.8965
G1 Z-0.01 F10
G2 X7.99 Y0.8728 I-0.237 J0 F40
G2 X7.9865 Y0.8497 I-0.2267 J0.0228
G2 X7.9807 Y0.8272 I-0.2234 J0.0459
G2 X7.9724 Y0.8048 I-0.2271 J0.0715
G2 X7.9623 Y0.7832 I-0.3646 J0.1563
G2 X7.9507 Y0.7617 I-0.3716 J0.1865
G2 X7.9379 Y0.7409 I-0.371 J0.2144
G2 X7.9234 Y0.7201 I-0.3849 J0.2528
G2 X7.9079 Y0.7 I-0.5278 J0.3898
G2 X7.8914 Y0.68 I-0.5353 J0.4282
G2 X7.8741 Y0.6607 I-0.5316 J0.4593
G2 X7.8556 Y0.6416 I-0.5427 J0.505
G1 X7.7815 Y0.57
G3 X7.7721 Y0.5611 I0.8861 J-0.9498
G3 X7.7618 Y0.5513 I1.0881 J-1.1433
G3 X7.7517 Y0.5413 I1.2053 J-1.2439
G3 X7.7406 Y0.5304 I1.4521 J-1.4741
G1 X7.6986 Y0.4858
G3 X7.6886 Y0.4744 I1.1214 J-0.996
G3 X7.6796 Y0.464 I0.9432 J-0.8208
G3 X7.6707 Y0.4534 I0.8658 J-0.7369
G3 X7.6628 Y0.4437 I0.7169 J-0.5953
G3 X7.6552 Y0.4338 I0.1702 J-0.1375
G3 X7.65 Y0.426 I0.0998 J-0.0716
G3 X7.6456 Y0.4178 I0.0757 J-0.046
G3 X7.6433 Y0.4117 I0.0391 J-0.0185
G1 X8.0176 Y0.4117
G1 X8.0176 Y0.2547
G1 X7.4373 Y0.2547
G2 X7.4363 Y0.2616 I0.1145 J0.0207
G2 X7.4354 Y0.2704 I0.1829 J0.0218
G2 X7.435 Y0.2792 I0.2268 J0.0161
G2 X7.4348 Y0.2899 I0.3344 J0.0107
G1 X7.4348 Y0.2998
G1 X7.4348 Y0.3081
G1 X7.4348 Y0.3148
G1 X7.4348 Y0.32
G2 X7.4361 Y0.3501 I0.3578 J0
G2 X7.4397 Y0.3778 I0.3087 J-0.026
G2 X7.4458 Y0.4051 I0.2878 J-0.0507
G2 X7.4543 Y0.4306 I0.2595 J-0.0719
G2 X7.4648 Y0.4553 I0.3938 J-0.1527
G2 X7.4767 Y0.4791 I0.3744 J-0.1733
G2 X7.4902 Y0.502 I0.3612 J-0.1964
G2 X7.5052 Y0.5241 I0.3512 J-0.2214
G2 X7.5213 Y0.5454 I0.4675 J-0.3386
G2 X7.5386 Y0.566 I0.457 J-0.3645
G2 X7.5568 Y0.5859 I0.4444 J-0.3892
G2 X7.5761 Y0.6051 I0.4388 J-0.4211
G1 X7.6546 Y0.6805
G3 X7.6697 Y0.6949 I-6.5538 J6.8409
G3 X7.6838 Y0.7086 I-5.8415 J6.0705
G3 X7.6979 Y0.7223 I-5.5113 J5.7006
G3 X7.7111 Y0.7351 I-4.8745 J5.0171
G3 X7.7242 Y0.7482 I-0.5266 J0.5392
G3 X7.7359 Y0.7606 I-0.4648 J0.4531
G3 X7.7474 Y0.7733 I-0.4416 J0.4084
G3 X7.7576 Y0.7854 I-0.388 J0.3389
G3 X7.7673 Y0.7979 I-0.2179 J0.1789
G3 X7.7755 Y0.8099 I-0.1921 J0.1404
G3 X7.7829 Y0.8223 I-0.1846 J0.1184
G3 X7.789 Y0.8343 I-0.1653 J0.0911
G3 X7.794 Y0.8468 I-0.1205 J0.0556
G3 X7.7975 Y0.859 I-0.1118 J0.0384
G3 X7.7996 Y0.8715 I-0.111 J0.0253
G3 X7.8003 Y0.884 I-0.1104 J0.0125
G3 X7.798 Y0.9112 I-0.1655 J0
G3 X7.7924 Y0.9311 I-0.0937 J-0.0156
G3 X7.7824 Y0.9488 I-0.0702 J-0.0283
G3 X7.7689 Y0.9618 I-0.0509 J-0.0389
G3 X7.7525 Y0.9716 I-0.064 J-0.0889
G3 X7.7337 Y0.9788 I-0.0523 J-0.1085
G3 X7.714 Y0.9829 I-0.0372 J-0.1282
G3 X7.691 Y0.9844 I-0.023 J-0.1742
G3 X7.6741 Y0.9838 I0 J-0.2082
G3 X7.6582 Y0.9818 I0.0152 J-0.1867
G3 X7.6426 Y0.9784 I0.0299 J-0.1776
G3 X7.6276 Y0.9738 I0.0434 J-0.166
G3 X7.613 Y0.9681 I0.124 J-0.3427
G3 X7.5994 Y0.9621 I0.1248 J-0.3035
G3 X7.586 Y0.9556 I0.1317 J-0.2831
G3 X7.5736 Y0.9486 I0.1322 J-0.2521
G3 X7.5615 Y0.9412 I0.2233 J-0.3784
G3 X7.5508 Y0.9342 I0.1896 J-0.2988
G3 X7.5405 Y0.9268 I0.179 J-0.2609
G3 X7.5315 Y0.9198 I0.1505 J-0.2014
G3 X7.5228 Y0.9125 I0.4664 J-0.5685
G3 X7.5161 Y0.9067 I0.2834 J-0.3348
G3 X7.5096 Y0.9008 I0.2157 J-0.2447
G3 X7.5052 Y0.8965 I0.1113 J-0.1196
G1 X7.4122 Y1.0271
G2 X7.4408 Y1.0516 I0.3033 J-0.3254
G2 X7.472 Y1.0741 I0.2859 J-0.3642
G2 X7.505 Y1.0939 I0.2634 J-0.4
G2 X7.541 Y1.1119 I0.2525 J-0.4598
G2 X7.5784 Y1.1265 I0.1528 J-0.3372
G2 X7.6171 Y1.1369 I0.115 J-0.3507
G2 X7.6567 Y1.1431 I0.078 J-0.3692
G2 X7.6986 Y1.1452 I0.0419 J-0.4097
G2 X7.7368 Y1.1441 I0 J-0.6209
G2 X7.7698 Y1.1408 I-0.0288 J-0.4669
G2 X7.8025 Y1.1351 I-0.0537 J-0.4022
G2 X7.8304 Y1.1276 I-0.0656 J-0.3011
G2 X7.8576 Y1.1176 I-0.0975 J-0.3061
G2 X7.8808 Y1.1064 I-0.0996 J-0.2366
G2 X7.9028 Y1.0929 I-0.1113 J-0.2042
G2 X7.9215 Y1.078 I-0.1143 J-0.1635
G2 X7.9383 Y1.0611 I-0.1312 J-0.1467
G2 X7.9526 Y1.0425 I-0.1419 J-0.1243
G2 X7.9645 Y1.0224 I-0.1576 J-0.1068
G2 X7.9743 Y1.0002 I-0.1832 J-0.0937
G2 X7.9816 Y0.9769 I-0.2296 J-0.085
G2 X7.987 Y0.9513 I-0.2735 J-0.0712
G2 X7.9901 Y0.9253 I-0.3042 J-0.0498
G2 X7.9912 Y0.8965 I-0.3724 J-0.0288
G0 Z0.125
(remove M5 and M30)

2518
test/gcode/braid_cut2d.nc Executable file

File diff suppressed because it is too large Load Diff

25
test/test.py Normal file
View File

@ -0,0 +1,25 @@
import random
import serial
import time
ser = serial.Serial('/dev/tty.usbmodem24111', 115200, timeout=0.001)
time.sleep(1)
outstanding = 0
data = ''
while True:
time.sleep(0.1)
data += ser.read()
pos = data.find('\n')
if pos == -1:
line = ''
else:
line = data[0:pos + 1]
data = data[pos + 1:]
if line == '' and outstanding < 3:
while outstanding < 3:
ser.write("G0 Z%0.3f\n" % (0.01 * (random.random() - 0.5)))
#ser.write("M3\n")
outstanding += 1
continue
if line == 'ok\r\n':
outstanding -= 1
print outstanding, repr(line.rstrip())