File re-organization. New Makefile.
- Re-organized source code files into a ‘grbl’ directory to lessen one step in compiling Grbl through the Arduino IDE. - Added an ‘examples’ directory with an upload .INO sketch to further simplify compiling and uploading Grbl via the Arduino IDE. - Updated the Makefile with regard to the source code no longer being in the root directory. All files generated by compiling is placed in a separate ‘build’ directory to keep things tidy. The makefile should operate in the same way as it did before.
This commit is contained in:
325
grbl/config.h
Normal file
325
grbl/config.h
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
config.h - compile time configuration
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2013-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2013 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
// This file contains compile-time configurations for Grbl's internal system. For the most part,
|
||||
// users will not need to directly modify these, but they are here for specific needs, i.e.
|
||||
// performance tuning or adjusting to non-typical machines.
|
||||
|
||||
// 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 115200
|
||||
|
||||
// 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 realtime command special characters. These characters are 'picked-off' directly from the
|
||||
// serial read data stream and are not passed to the grbl line execution parser. Select characters
|
||||
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
|
||||
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
|
||||
// g-code programs, maybe selected for interface programs.
|
||||
// NOTE: If changed, manually update help message in report.c.
|
||||
#define CMD_STATUS_REPORT '?'
|
||||
#define CMD_FEED_HOLD '!'
|
||||
#define CMD_CYCLE_START '~'
|
||||
#define CMD_RESET 0x18 // ctrl-x.
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
|
||||
// After homing, Grbl will set by default the entire machine space into negative space, as is typical
|
||||
// for professional CNC machines, regardless of where the limit switches are located. Uncomment this
|
||||
// define to force Grbl to always set the machine origin at the homed location despite switch orientation.
|
||||
// #define HOMING_FORCE_SET_ORIGIN // Uncomment to enable.
|
||||
|
||||
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
|
||||
// and addresses are defined in settings.h. With the current settings, up to 2 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-2)
|
||||
|
||||
// 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
|
||||
|
||||
// If your machine has two limits switches wired in parallel to one axis, you will need to enable
|
||||
// this feature. Since the two switches are sharing a single pin, there is no way for Grbl to tell
|
||||
// which one is enabled. This option only effects homing, where if a limit is engaged, Grbl will
|
||||
// alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
|
||||
// limit switch for each axis, don't enable this option. By keeping it disabled, you can homing while
|
||||
// on the limit switch and not have to move the machine off of it.
|
||||
// #define LIMITS_TWO_SWITCHES_ON_AXES
|
||||
|
||||
// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
|
||||
// goes from 18 or 16 to make room for the additional line number data in the plan_block_t struct
|
||||
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.
|
||||
|
||||
// Allows GRBL to report the real-time feed rate. Enabling this means that GRBL will be reporting more
|
||||
// data with each status update.
|
||||
// NOTE: This is experimental and doesn't quite work 100%. Maybe fixed or refactored later.
|
||||
// #define REPORT_REALTIME_RATE // Disabled by default. Uncomment to enable.
|
||||
|
||||
// Upon a successful probe cycle, this option provides immediately feedback of the probe coordinates
|
||||
// through an automatically generated message. If disabled, users can still access the last probe
|
||||
// coordinates through Grbl '$#' print parameters.
|
||||
#define MESSAGE_PROBE_COORDINATES // Enabled by default. Comment to disable.
|
||||
|
||||
// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
|
||||
// analog pin 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.
|
||||
|
||||
// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
|
||||
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
|
||||
// #define HOMING_CYCLE_0 (1<<X_AXIS) and #define HOMING_CYCLE_1 (1<<Y_AXIS)
|
||||
// NOTE: This configuration option alters the motion of the X and Y axes to principle of operation
|
||||
// defined at (http://corexy.com/theory.html). Motors are assumed to positioned and wired exactly as
|
||||
// described, if not, motions may move in strange directions. Grbl assumes the CoreXY A and B motors
|
||||
// have the same steps per mm internally.
|
||||
// #define COREXY // Default disabled. Uncomment to enable.
|
||||
|
||||
// Inverts pin logic of the control command pins. This essentially means when this option is enabled
|
||||
// you can use normally-closed switches, rather than the default normally-open switches.
|
||||
// NOTE: Will eventually be added to Grbl settings in v1.0.
|
||||
// #define INVERT_CONTROL_PIN // Default disabled. Uncomment to enable.
|
||||
|
||||
// Enable input pin states feedback in status reports. The data is presented as a binary value with
|
||||
// the bits in the appropriate input pin ports being 0(low) or 1(high). Useful for setting up a new
|
||||
// CNC machine, but do not recommend keeping this option by default, as it will consume CPU resources
|
||||
// with little to no benefit during normal operation.
|
||||
// #define REPORT_INPUT_PIN_STATES // Default disabled. 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 the maximum step rate allowed to be written as a Grbl setting. This value is strictly limited
|
||||
// by the CPU speed and will change if something other than an AVR running at 16MHz is used.
|
||||
// NOTE: For now disabled, will enable if flash space permits.
|
||||
// #define MAX_STEP_RATE_HZ 30000 // Hz
|
||||
|
||||
// By default, Grbl sets all input pins to normal-high operation with their internal pull-up resistors
|
||||
// enabled. This simplifies the wiring for users by requiring only a switch connected to ground,
|
||||
// although its recommended that users take the extra step of wiring in low-pass filter to reduce
|
||||
// electrical noise detected by the pin. If the user inverts the pin in Grbl settings, this just flips
|
||||
// which high or low reading indicates an active signal. In normal operation, this means the user
|
||||
// needs to connect a normal-open switch, but if inverted, this means the user should connect a
|
||||
// normal-closed switch.
|
||||
// The following options disable the internal pull-up resistors, sets the pins to a normal-low
|
||||
// operation, and switches must be now connect to Vcc instead of ground. This also flips the meaning
|
||||
// of the invert pin Grbl setting, where an inverted setting now means the user should connect a
|
||||
// normal-open switch and vice versa.
|
||||
// NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes.
|
||||
// WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors!
|
||||
//#define DISABLE_LIMIT_PIN_PULL_UP
|
||||
//#define DISABLE_PROBE_PIN_PULL_UP
|
||||
//#define DISABLE_CONTROL_PIN_PULL_UP
|
||||
|
||||
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
|
||||
// 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.
|
||||
|
||||
// Used 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.
|
||||
|
||||
// Used by variable spindle output only. This forces the PWM output to a minimum duty cycle when enabled.
|
||||
// When disabled, the PWM pin will still read 0V. Most users will not need this option, but it may be
|
||||
// useful in certain scenarios. This setting does not update the minimum spindle RPM calculations. Any
|
||||
// spindle RPM output lower than this value will be set to this value.
|
||||
// #define MINIMUM_SPINDLE_PWM 5 // Default disabled. Uncomment to enable. Integer (0-255)
|
||||
|
||||
// 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)
|
||||
|
||||
// Sets the minimum feed rate the planner will allow. Any value below it will be set to this minimum
|
||||
// value. This also ensures that a planned motion always completes and accounts for any floating-point
|
||||
// round-off errors. A lower value than 1.0 mm/min may work in some cases, but we don't recommend it.
|
||||
#define MINIMUM_FEED_RATE 1.0 // (mm/min)
|
||||
|
||||
// Number of arc generation iterations by small angle approximation before exact arc trajectory
|
||||
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
|
||||
// are issues with the accuracy of the arc generations, or increased if arc execution is getting
|
||||
// bogged down by too many trig calculations.
|
||||
#define N_ARC_CORRECTION 12 // Integer (1-255)
|
||||
|
||||
// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
|
||||
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
|
||||
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
|
||||
// run-time command executions, like status reports, since these are performed between each dwell
|
||||
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
|
||||
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
|
||||
|
||||
// Creates a delay between the direction pin setting and corresponding step pulse by creating
|
||||
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
|
||||
// sets the direction pins, and does not immediately set the stepper pins, as it would in
|
||||
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
|
||||
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
|
||||
// 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
|
||||
// available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino
|
||||
// begins to crash due to the lack of available RAM or if the CPU is having trouble keeping
|
||||
// up with planning new incoming motions as they are executed.
|
||||
// #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h.
|
||||
|
||||
// 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: 80 characters is not a problem except for extreme cases, but the line buffer size
|
||||
// can be too small and g-code blocks can get truncated. Officially, the g-code standards
|
||||
// support up to 256 characters. In future versions, this default will be increased, when
|
||||
// we know how much extra memory space we can re-invest into this.
|
||||
// #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h
|
||||
|
||||
// Serial send and receive buffer size. The receive buffer is often used as another streaming
|
||||
// buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming
|
||||
// interfaces will character count and track each block send to each block response. So,
|
||||
// increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable
|
||||
// memory allows. The send buffer primarily handles messages in Grbl. Only increase if large
|
||||
// messages are sent and Grbl begins to stall, waiting to send the rest of the message.
|
||||
// NOTE: Buffer size values must be greater than zero and less than 256.
|
||||
// #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h
|
||||
// #define TX_BUFFER_SIZE 64
|
||||
|
||||
// Toggles XON/XOFF software flow control for serial communications. Not officially supported
|
||||
// due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware
|
||||
// on these chips do not support XON/XOFF flow control characters and the intermediate buffer
|
||||
// in the chips cause latency and overflow problems with standard terminal programs. However,
|
||||
// using specifically-programmed UI's to manage this latency problem has been confirmed to work.
|
||||
// As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard
|
||||
// terminal programs since their firmware correctly manage these XON/XOFF characters. In any
|
||||
// case, please report any successes to grbl administrators!
|
||||
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
|
||||
|
||||
// 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:
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#endif
|
64
grbl/coolant_control.c
Normal file
64
grbl/coolant_control.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
coolant_control.c - coolant control methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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 "grbl.h"
|
||||
|
||||
|
||||
void coolant_init()
|
||||
{
|
||||
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
|
||||
#ifdef ENABLE_M7
|
||||
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
|
||||
#endif
|
||||
coolant_stop();
|
||||
}
|
||||
|
||||
|
||||
void coolant_stop()
|
||||
{
|
||||
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
|
||||
#ifdef ENABLE_M7
|
||||
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void coolant_set_state(uint8_t mode)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void coolant_run(uint8_t mode)
|
||||
{
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
|
||||
coolant_set_state(mode);
|
||||
}
|
30
grbl/coolant_control.h
Normal file
30
grbl/coolant_control.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
coolant_control.h - spindle control methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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 coolant_control_h
|
||||
#define coolant_control_h
|
||||
|
||||
|
||||
void coolant_init();
|
||||
void coolant_stop();
|
||||
void coolant_set_state(uint8_t mode);
|
||||
void coolant_run(uint8_t mode);
|
||||
|
||||
#endif
|
268
grbl/cpu_map.h
Normal file
268
grbl/cpu_map.h
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
cpu_map.h - CPU and pin mapping configuration file
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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 (CONTROL).
|
||||
#define LIMIT_DDR DDRB
|
||||
#define LIMIT_PIN PINB
|
||||
#define LIMIT_PORT PORTB
|
||||
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
|
||||
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
|
||||
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
|
||||
#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 controls (cycle start, reset, feed hold) input pins.
|
||||
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
|
||||
#define CONTROL_DDR DDRC
|
||||
#define CONTROL_PIN PINC
|
||||
#define CONTROL_PORT PORTC
|
||||
#define RESET_BIT 0 // Uno Analog Pin 0
|
||||
#define FEED_HOLD_BIT 1 // Uno Analog Pin 1
|
||||
#define CYCLE_START_BIT 2 // Uno Analog Pin 2
|
||||
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin
|
||||
#define CONTROL_INT_vect PCINT1_vect
|
||||
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
|
||||
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT))
|
||||
|
||||
// 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 PWM_MAX_VALUE 255.0
|
||||
#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 DDRC
|
||||
#define DIRECTION_PORT PORTC
|
||||
#define DIRECTION_PIN PINC
|
||||
#define X_DIRECTION_BIT 7 // MEGA2560 Digital Pin 30
|
||||
#define Y_DIRECTION_BIT 6 // MEGA2560 Digital Pin 31
|
||||
#define Z_DIRECTION_BIT 5 // MEGA2560 Digital Pin 32
|
||||
#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 CONTROLs (cycle start, reset, feed hold) input pins.
|
||||
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
|
||||
#define CONTROL_DDR DDRK
|
||||
#define CONTROL_PIN PINK
|
||||
#define CONTROL_PORT PORTK
|
||||
#define RESET_BIT 0 // MEGA2560 Analog Pin 8
|
||||
#define FEED_HOLD_BIT 1 // MEGA2560 Analog Pin 9
|
||||
#define CYCLE_START_BIT 2 // MEGA2560 Analog Pin 10
|
||||
#define CONTROL_INT PCIE2 // Pin change interrupt enable pin
|
||||
#define CONTROL_INT_vect PCINT2_vect
|
||||
#define CONTROL_PCMSK PCMSK2 // Pin change interrupt register
|
||||
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT))
|
||||
|
||||
// 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 TIMER4B which is attached to Digital Pin 7
|
||||
#define PWM_MAX_VALUE 65535.0
|
||||
#define TCCRA_REGISTER TCCR4A
|
||||
#define TCCRB_REGISTER TCCR4B
|
||||
#define OCR_REGISTER OCR4B
|
||||
|
||||
#define COMB_BIT COM4B1
|
||||
#define WAVE0_REGISTER WGM40
|
||||
#define WAVE1_REGISTER WGM41
|
||||
#define WAVE2_REGISTER WGM42
|
||||
#define WAVE3_REGISTER WGM43
|
||||
|
||||
#define SPINDLE_PWM_DDR DDRH
|
||||
#define SPINDLE_PWM_PORT PORTH
|
||||
#define SPINDLE_PWM_BIT 4 // MEGA2560 Digital Pin 97
|
||||
#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
|
302
grbl/defaults.h
Normal file
302
grbl/defaults.h
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
defaults.h - defaults settings configuration file
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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 defaults.h file serves as a central default settings file for different machine
|
||||
types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
|
||||
here are supplied by users, so your results may vary. However, this should give you
|
||||
a good starting point as you get to know your machine and tweak the settings for your
|
||||
our nefarious needs. */
|
||||
|
||||
#ifndef defaults_h
|
||||
#define defaults_h
|
||||
|
||||
#ifdef DEFAULTS_GENERIC
|
||||
// Grbl generic default settings. Should work across different machines.
|
||||
#define DEFAULT_X_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_Y_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_Z_STEPS_PER_MM 250.0
|
||||
#define DEFAULT_X_MAX_RATE 500.0 // mm/min
|
||||
#define DEFAULT_Y_MAX_RATE 500.0 // mm/min
|
||||
#define DEFAULT_Z_MAX_RATE 500.0 // mm/min
|
||||
#define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_X_MAX_TRAVEL 200.0 // mm
|
||||
#define DEFAULT_Y_MAX_TRAVEL 200.0 // mm
|
||||
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK 0
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // 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 500.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef DEFAULTS_SHERLINE_5400
|
||||
// Description: Sherline 5400 mill with three NEMA 23 Keling KL23H256-21-8B 185 oz-in stepper motors,
|
||||
// driven by three Pololu A4988 stepper drivers with a 30V, 6A power supply at 1.5A per winding.
|
||||
#define MICROSTEPS 2
|
||||
#define STEPS_PER_REV 200.0
|
||||
#define MM_PER_REV (0.050*MM_PER_INCH) // 0.050 inch/rev leadscrew
|
||||
#define DEFAULT_X_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Y_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Z_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_X_MAX_RATE 635.0 // mm/min (25 ipm)
|
||||
#define DEFAULT_Y_MAX_RATE 635.0 // mm/min
|
||||
#define DEFAULT_Z_MAX_RATE 635.0 // mm/min
|
||||
#define DEFAULT_X_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
|
||||
#define DEFAULT_Y_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
|
||||
#define DEFAULT_Z_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2
|
||||
#define DEFAULT_X_MAX_TRAVEL 225.0 // mm
|
||||
#define DEFAULT_Y_MAX_TRAVEL 125.0 // mm
|
||||
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
|
||||
#define DEFAULT_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_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
|
||||
#endif
|
||||
|
||||
#ifdef DEFAULTS_SHAPEOKO
|
||||
// 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 400
|
||||
#define MM_PER_REV_XY (0.08*18*MM_PER_INCH) // 0.08 in belt pitch, 18 pulley teeth
|
||||
#define MICROSTEPS_Z 2
|
||||
#define STEP_REVS_Z 400
|
||||
#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 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_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
|
||||
#define DEFAULT_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_SHAPEOKO_2
|
||||
// Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos
|
||||
// grblShield at 28V.
|
||||
#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 5000.0 // mm/min
|
||||
#define DEFAULT_Y_MAX_RATE 5000.0 // mm/min
|
||||
#define DEFAULT_Z_MAX_RATE 500.0 // mm/min
|
||||
#define DEFAULT_X_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2
|
||||
#define DEFAULT_Y_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2
|
||||
#define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2
|
||||
#define DEFAULT_X_MAX_TRAVEL 290.0 // mm
|
||||
#define DEFAULT_Y_MAX_TRAVEL 290.0 // mm
|
||||
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Z_AXIS))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // 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
|
||||
// Description: Zen Toolworks 7x7 mill with three Shinano SST43D2121 65oz-in NEMA 17 stepper motors.
|
||||
// Leadscrew is different from some ZTW kits, where most are 1.25mm/rev rather than 8.0mm/rev here.
|
||||
// Driven by 30V, 6A power supply and TI DRV8811 stepper motor drivers.
|
||||
#define MICROSTEPS 8
|
||||
#define STEPS_PER_REV 200.0
|
||||
#define MM_PER_REV 8.0 // 8 mm/rev leadscrew
|
||||
#define DEFAULT_X_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Y_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_Z_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV)
|
||||
#define DEFAULT_X_MAX_RATE 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_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS))
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
|
||||
#define DEFAULT_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_OXCNC
|
||||
// Grbl settings for OpenBuilds OX CNC Machine
|
||||
// http://www.openbuilds.com/builds/openbuilds-ox-cnc-machine.341/
|
||||
#define DEFAULT_X_STEPS_PER_MM 26.670
|
||||
#define DEFAULT_Y_STEPS_PER_MM 26.670
|
||||
#define DEFAULT_Z_STEPS_PER_MM 50
|
||||
#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 500.0 // mm
|
||||
#define DEFAULT_Y_MAX_TRAVEL 750.0 // mm
|
||||
#define DEFAULT_Z_MAX_TRAVEL 80.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK 0
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // 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 500.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#endif
|
||||
|
||||
#ifdef DEFAULTS_SIMULATOR
|
||||
// Settings only for Grbl Simulator (www.github.com/grbl/grbl-sim)
|
||||
// Grbl generic default settings. Should work across different machines.
|
||||
#define DEFAULT_X_STEPS_PER_MM 1000.0
|
||||
#define DEFAULT_Y_STEPS_PER_MM 1000.0
|
||||
#define DEFAULT_Z_STEPS_PER_MM 1000.0
|
||||
#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 (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_Y_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_Z_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
|
||||
#define DEFAULT_X_MAX_TRAVEL 1000.0 // mm
|
||||
#define DEFAULT_Y_MAX_TRAVEL 1000.0 // mm
|
||||
#define DEFAULT_Z_MAX_TRAVEL 1000.0 // mm
|
||||
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
|
||||
#define DEFAULT_STEPPING_INVERT_MASK 0
|
||||
#define DEFAULT_DIRECTION_INVERT_MASK 0
|
||||
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled)
|
||||
#define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION))
|
||||
#define DEFAULT_JUNCTION_DEVIATION 0.02 // mm
|
||||
#define DEFAULT_ARC_TOLERANCE 0.002 // 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 500.0 // mm/min
|
||||
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
|
||||
#define DEFAULT_HOMING_PULLOFF 1.0 // mm
|
||||
#endif
|
||||
|
||||
#endif
|
151
grbl/eeprom.c
Normal file
151
grbl/eeprom.c
Normal file
@ -0,0 +1,151 @@
|
||||
// This file has been prepared for Doxygen automatic documentation generation.
|
||||
/*! \file ********************************************************************
|
||||
*
|
||||
* Atmel Corporation
|
||||
*
|
||||
* \li File: eeprom.c
|
||||
* \li Compiler: IAR EWAAVR 3.10c
|
||||
* \li Support mail: avr@atmel.com
|
||||
*
|
||||
* \li Supported devices: All devices with split EEPROM erase/write
|
||||
* capabilities can be used.
|
||||
* The example is written for ATmega48.
|
||||
*
|
||||
* \li AppNote: AVR103 - Using the EEPROM Programming Modes.
|
||||
*
|
||||
* \li Description: Example on how to use the split EEPROM erase/write
|
||||
* capabilities in e.g. ATmega48. All EEPROM
|
||||
* programming modes are tested, i.e. Erase+Write,
|
||||
* Erase-only and Write-only.
|
||||
*
|
||||
* $Revision: 1.6 $
|
||||
* $Date: Friday, February 11, 2005 07:16:44 UTC $
|
||||
****************************************************************************/
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
/* These EEPROM bits have different names on different devices. */
|
||||
#ifndef EEPE
|
||||
#define EEPE EEWE //!< EEPROM program/write enable.
|
||||
#define EEMPE EEMWE //!< EEPROM master program/write enable.
|
||||
#endif
|
||||
|
||||
/* These two are unfortunately not defined in the device include files. */
|
||||
#define EEPM1 5 //!< EEPROM Programming Mode Bit 1.
|
||||
#define EEPM0 4 //!< EEPROM Programming Mode Bit 0.
|
||||
|
||||
/* Define to reduce code size. */
|
||||
#define EEPROM_IGNORE_SELFPROG //!< Remove SPM flag polling.
|
||||
|
||||
/*! \brief Read byte from EEPROM.
|
||||
*
|
||||
* This function reads one byte from a given EEPROM address.
|
||||
*
|
||||
* \note The CPU is halted for 4 clock cycles during EEPROM read.
|
||||
*
|
||||
* \param addr EEPROM address to read from.
|
||||
* \return The byte read from the EEPROM address.
|
||||
*/
|
||||
unsigned char eeprom_get_char( unsigned int addr )
|
||||
{
|
||||
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
|
||||
EEAR = addr; // Set EEPROM address register.
|
||||
EECR = (1<<EERE); // Start EEPROM read operation.
|
||||
return EEDR; // Return the byte read from EEPROM.
|
||||
}
|
||||
|
||||
/*! \brief Write byte to EEPROM.
|
||||
*
|
||||
* This function writes one byte to a given EEPROM address.
|
||||
* The differences between the existing byte and the new value is used
|
||||
* to select the most efficient EEPROM programming mode.
|
||||
*
|
||||
* \note The CPU is halted for 2 clock cycles during EEPROM programming.
|
||||
*
|
||||
* \note When this function returns, the new EEPROM value is not available
|
||||
* until the EEPROM programming time has passed. The EEPE bit in EECR
|
||||
* should be polled to check whether the programming is finished.
|
||||
*
|
||||
* \note The EEPROM_GetChar() function checks the EEPE bit automatically.
|
||||
*
|
||||
* \param addr EEPROM address to write to.
|
||||
* \param new_value New EEPROM value.
|
||||
*/
|
||||
void eeprom_put_char( unsigned int addr, unsigned char new_value )
|
||||
{
|
||||
char old_value; // Old EEPROM value.
|
||||
char diff_mask; // Difference mask, i.e. old value XOR new value.
|
||||
|
||||
cli(); // Ensure atomic operation for the write operation.
|
||||
|
||||
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
|
||||
#ifndef EEPROM_IGNORE_SELFPROG
|
||||
do {} while( SPMCSR & (1<<SELFPRGEN) ); // Wait for completion of SPM.
|
||||
#endif
|
||||
|
||||
EEAR = addr; // Set EEPROM address register.
|
||||
EECR = (1<<EERE); // Start EEPROM read operation.
|
||||
old_value = EEDR; // Get old EEPROM value.
|
||||
diff_mask = old_value ^ new_value; // Get bit differences.
|
||||
|
||||
// Check if any bits are changed to '1' in the new value.
|
||||
if( diff_mask & new_value ) {
|
||||
// Now we know that _some_ bits need to be erased to '1'.
|
||||
|
||||
// Check if any bits in the new value are '0'.
|
||||
if( new_value != 0xff ) {
|
||||
// Now we know that some bits need to be programmed to '0' also.
|
||||
|
||||
EEDR = new_value; // Set EEPROM data register.
|
||||
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||
(0<<EEPM1) | (0<<EEPM0); // ...and Erase+Write mode.
|
||||
EECR |= (1<<EEPE); // Start Erase+Write operation.
|
||||
} else {
|
||||
// Now we know that all bits should be erased.
|
||||
|
||||
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||
(1<<EEPM0); // ...and Erase-only mode.
|
||||
EECR |= (1<<EEPE); // Start Erase-only operation.
|
||||
}
|
||||
} else {
|
||||
// Now we know that _no_ bits need to be erased to '1'.
|
||||
|
||||
// Check if any bits are changed from '1' in the old value.
|
||||
if( diff_mask ) {
|
||||
// Now we know that _some_ bits need to the programmed to '0'.
|
||||
|
||||
EEDR = new_value; // Set EEPROM data register.
|
||||
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||
(1<<EEPM1); // ...and Write-only mode.
|
||||
EECR |= (1<<EEPE); // Start Write-only operation.
|
||||
}
|
||||
}
|
||||
|
||||
sei(); // Restore interrupt flag state.
|
||||
}
|
||||
|
||||
// Extensions added as part of Grbl
|
||||
|
||||
|
||||
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) {
|
||||
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
|
37
grbl/eeprom.h
Normal file
37
grbl/eeprom.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
eeprom.h - EEPROM methods
|
||||
Part of Grbl
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
GRBL(tm) - Embedded CNC g-code interpreter and motion-controller
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef eeprom_h
|
||||
#define eeprom_h
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
29
grbl/examples/grblUpload/grblUpload.ino
Normal file
29
grbl/examples/grblUpload/grblUpload.ino
Normal file
@ -0,0 +1,29 @@
|
||||
/***********************************************************************
|
||||
This sketch compiles and uploads Grbl to your 328p-based Arduino!
|
||||
|
||||
To use:
|
||||
- First make sure you have imported Grbl source code into your Arduino
|
||||
IDE. There are details on our Github website on how to do this.
|
||||
|
||||
- Select your Arduino Board and Serial Port in the Tools drop-down menu.
|
||||
NOTE: Grbl only officially supports 328p-based Arduinos, like the Uno.
|
||||
Using other boards will likely not work!
|
||||
|
||||
- Then just click 'Upload'. That's it!
|
||||
|
||||
For advanced users:
|
||||
If you'd like to see what else Grbl can do, there are some additional
|
||||
options for customization and features you can enable or disable.
|
||||
Navigate your file system to where the Arduino IDE has stored the Grbl
|
||||
source code files, open the 'config.h' file in your favorite text
|
||||
editor. Inside are dozens of feature descriptions and #defines. Simply
|
||||
comment or uncomment the #defines or alter their assigned values, save
|
||||
your changes, and then click 'Upload' here.
|
||||
|
||||
Copyright (c) 2015 Sungeun K. Jeon
|
||||
Released under the MIT-license. See license.txt for details.
|
||||
***********************************************************************/
|
||||
|
||||
#include <grbl.h>
|
||||
|
||||
// Do not alter this file!
|
21
grbl/examples/grblUpload/license.txt
Normal file
21
grbl/examples/grblUpload/license.txt
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 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.
|
1061
grbl/gcode.c
Normal file
1061
grbl/gcode.c
Normal file
File diff suppressed because it is too large
Load Diff
207
grbl/gcode.h
Normal file
207
grbl/gcode.h
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
gcode.h - rs274/ngc parser.
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef gcode_h
|
||||
#define gcode_h
|
||||
|
||||
|
||||
// Define modal group internal numbers for checking multiple command violations and tracking the
|
||||
// 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).
|
||||
// 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_G7 6 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED.
|
||||
#define MODAL_GROUP_G8 7 // [G43,G43.1,G49] Tool length offset
|
||||
#define MODAL_GROUP_G12 8 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
|
||||
|
||||
#define MODAL_GROUP_M4 9 // [M0,M1,M2,M30] Stopping
|
||||
#define MODAL_GROUP_M7 10 // [M3,M4,M5] Spindle turning
|
||||
#define MODAL_GROUP_M8 11 // [M7,M8,M9] Coolant control
|
||||
|
||||
#define OTHER_INPUT_F 12
|
||||
#define OTHER_INPUT_S 13
|
||||
#define OTHER_INPUT_T 14
|
||||
|
||||
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
|
||||
// internally by the parser to know which command to execute.
|
||||
|
||||
// Modal Group G0: Non-modal actions
|
||||
#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero)
|
||||
#define NON_MODAL_DWELL 1 // G4
|
||||
#define NON_MODAL_SET_COORDINATE_DATA 2 // G10
|
||||
#define NON_MODAL_GO_HOME_0 3 // G28
|
||||
#define NON_MODAL_SET_HOME_0 4 // G28.1
|
||||
#define NON_MODAL_GO_HOME_1 5 // G30
|
||||
#define NON_MODAL_SET_HOME_1 6 // G30.1
|
||||
#define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53
|
||||
#define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92
|
||||
#define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1
|
||||
|
||||
// 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_TOWARD 4 // G38.2
|
||||
#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 5 // G38.3
|
||||
#define MOTION_MODE_PROBE_AWAY 6 // G38.4
|
||||
#define MOTION_MODE_PROBE_AWAY_NO_ERROR 7 // G38.5
|
||||
#define MOTION_MODE_NONE 8 // 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 G7: Cutter radius compensation mode
|
||||
#define CUTTER_COMP_DISABLE 0 // G40 (Default: Must be zero)
|
||||
|
||||
// Modal Group M7: Spindle control
|
||||
#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero)
|
||||
#define SPINDLE_ENABLE_CW 1 // M3
|
||||
#define SPINDLE_ENABLE_CCW 2 // M4
|
||||
|
||||
// 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,G38.2,G80}
|
||||
uint8_t feed_rate; // {G93,G94}
|
||||
uint8_t units; // {G20,G21}
|
||||
uint8_t distance; // {G90,G91}
|
||||
uint8_t plane_select; // {G17,G18,G19}
|
||||
// uint8_t cutter_comp; // {G40} NOTE: Don't need to track since it's always disabled.
|
||||
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 {
|
||||
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.
|
||||
int32_t line_number; // Last line number sent
|
||||
|
||||
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
|
||||
|
||||
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
|
||||
// position in mm. Loaded from EEPROM when called.
|
||||
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
|
||||
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
|
||||
float tool_length_offset; // Tracks tool length offset value when enabled.
|
||||
} parser_state_t;
|
||||
extern parser_state_t gc_state;
|
||||
|
||||
typedef struct {
|
||||
// uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated.
|
||||
// uint16_t value_words;
|
||||
|
||||
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();
|
||||
|
||||
// Execute one block of rs275/ngc/g-code
|
||||
uint8_t gc_execute_line(char *line);
|
||||
|
||||
// Set g-code parser position. Input in steps.
|
||||
void gc_sync_position();
|
||||
|
||||
#endif
|
62
grbl/grbl.h
Normal file
62
grbl/grbl.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
grbl.h - main Grbl include file
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2015 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 grbl_h
|
||||
#define grbl_h
|
||||
|
||||
// Grbl versioning system
|
||||
#define GRBL_VERSION "0.9h"
|
||||
#define GRBL_VERSION_BUILD "20150210"
|
||||
|
||||
// Define 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 the Grbl system include files. NOTE: Do not alter organization.
|
||||
#include "config.h"
|
||||
#include "nuts_bolts.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "defaults.h"
|
||||
#include "cpu_map.h"
|
||||
#include "coolant_control.h"
|
||||
#include "eeprom.h"
|
||||
#include "gcode.h"
|
||||
#include "limits.h"
|
||||
#include "motion_control.h"
|
||||
#include "planner.h"
|
||||
#include "print.h"
|
||||
#include "probe.h"
|
||||
#include "protocol.h"
|
||||
#include "report.h"
|
||||
#include "serial.h"
|
||||
#include "spindle_control.h"
|
||||
#include "stepper.h"
|
||||
|
||||
#endif
|
338
grbl/limits.c
Normal file
338
grbl/limits.c
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
limits.c - code pertaining to limit-switches and performing the homing cycle
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Homing axis search distance multiplier. Computed by this value times the axis max travel.
|
||||
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
|
||||
|
||||
|
||||
void limits_init()
|
||||
{
|
||||
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
|
||||
|
||||
#ifdef DISABLE_LIMIT_PIN_PULL_UP
|
||||
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
|
||||
#else
|
||||
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
|
||||
#endif
|
||||
|
||||
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
|
||||
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
|
||||
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
|
||||
} else {
|
||||
limits_disable();
|
||||
}
|
||||
|
||||
#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. 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.
|
||||
#ifndef ENABLE_SOFTWARE_DEBOUNCE
|
||||
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
|
||||
{
|
||||
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
|
||||
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
|
||||
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
|
||||
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
|
||||
// limit setting if their limits are constantly triggering after a reset and move their axes.
|
||||
if (sys.state != STATE_ALARM) {
|
||||
if (!(sys.rt_exec_alarm)) {
|
||||
mc_reset(); // Initiate system kill.
|
||||
bit_true_atomic(sys.rt_exec_alarm, (EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // OPTIONAL: Software debounce limit pin routine.
|
||||
// Upon limit pin change, enable watchdog timer to create a short delay.
|
||||
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
|
||||
ISR(WDT_vect) // Watchdog timer ISR
|
||||
{
|
||||
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
|
||||
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
|
||||
if (!(sys.rt_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.rt_exec_alarm, (EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
|
||||
// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate
|
||||
// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
|
||||
// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
|
||||
// circumvent the processes for executing motions in normal operation.
|
||||
// NOTE: Only the abort realtime command can interrupt this process.
|
||||
// TODO: Move limit pin-specific calls to a general function for portability.
|
||||
void limits_go_home(uint8_t cycle_mask)
|
||||
{
|
||||
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];
|
||||
|
||||
uint8_t limit_pin[N_AXIS], step_pin[N_AXIS];
|
||||
float max_travel = 0.0;
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
// Initialize limit and step pin masks
|
||||
limit_pin[idx] = get_limit_pin_mask(idx);
|
||||
step_pin[idx] = get_step_pin_mask(idx);
|
||||
#ifdef COREXY
|
||||
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
|
||||
#endif
|
||||
|
||||
// Determine travel distance to the furthest homing switch based on user max travel settings.
|
||||
// NOTE: settings.max_travel[] is stored as a negative value.
|
||||
if (max_travel > settings.max_travel[idx]) { max_travel = settings.max_travel[idx]; }
|
||||
}
|
||||
max_travel *= -HOMING_AXIS_SEARCH_SCALAR; // Ensure homing switches engaged by over-estimating max travel.
|
||||
|
||||
plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions.
|
||||
plan_sync_position(); // Sync planner position to current machine position.
|
||||
|
||||
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; }
|
||||
|
||||
// Initialize and declare variables needed for homing routine.
|
||||
uint8_t n_active_axis = 0;
|
||||
uint8_t axislock = 0;
|
||||
|
||||
system_convert_array_steps_to_mpos(target,sys.position);
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
// Set target location for active axes and setup computation for homing rate.
|
||||
if (bit_istrue(cycle_mask,bit(idx))) {
|
||||
n_active_axis++;
|
||||
if (approach) {
|
||||
// Set target direction based on cycle mask
|
||||
if (bit_istrue(settings.homing_dir_mask,bit(idx))) { target[idx] -= max_travel; }
|
||||
else { target[idx] += max_travel; }
|
||||
} else {
|
||||
// Set target direction based on cycle mask
|
||||
if (bit_istrue(settings.homing_dir_mask,bit(idx))) { target[idx] += max_travel; }
|
||||
else { target[idx] -= max_travel; }
|
||||
}
|
||||
}
|
||||
|
||||
// Apply axislock to the step port pins active in this cycle.
|
||||
if (bit_istrue(cycle_mask,bit(idx))) { axislock |= step_pin[idx]; }
|
||||
}
|
||||
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
|
||||
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; }
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
if (axislock & step_pin[idx]) {
|
||||
if (limit_state & limit_pin[idx]) { axislock &= ~(step_pin[idx]); }
|
||||
}
|
||||
}
|
||||
sys.homing_axis_lock = axislock;
|
||||
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
|
||||
// Check only for user reset. No time to run protocol_execute_realtime() in this loop.
|
||||
|
||||
if (sys.rt_exec_state & EXEC_RESET) { protocol_execute_realtime(); return; }
|
||||
} while (STEP_MASK & axislock);
|
||||
|
||||
st_reset(); // Immediately force kill steppers and reset step segment buffer.
|
||||
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared.
|
||||
plan_sync_position(); // Sync planner position to current machine position
|
||||
|
||||
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;
|
||||
|
||||
} 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.
|
||||
#ifdef COREXY
|
||||
int32_t off_axis_position = 0;
|
||||
#endif
|
||||
int32_t set_axis_position;
|
||||
// Set machine positions for homed limit switches. Don't update non-homed axes.
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
// NOTE: settings.max_travel[] is stored as a negative value.
|
||||
if (cycle_mask & bit(idx)) {
|
||||
set_axis_position = 0;
|
||||
#ifndef HOMING_FORCE_SET_ORIGIN
|
||||
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
|
||||
set_axis_position = lround(settings.max_travel[idx]*settings.steps_per_mm[idx]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COREXY
|
||||
if (idx==X_AXIS) {
|
||||
off_axis_position = (sys.position[B_MOTOR] - sys.position[A_MOTOR])/2;
|
||||
sys.position[A_MOTOR] = set_axis_position - off_axis_position;
|
||||
sys.position[B_MOTOR] = set_axis_position + off_axis_position;
|
||||
} else if (idx==Y_AXIS) {
|
||||
off_axis_position = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2;
|
||||
sys.position[A_MOTOR] = off_axis_position - set_axis_position;
|
||||
sys.position[B_MOTOR] = off_axis_position + set_axis_position;
|
||||
} else {
|
||||
sys.position[idx] = set_axis_position;
|
||||
}
|
||||
#else
|
||||
sys.position[idx] = set_axis_position;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
plan_sync_position(); // Sync planner position to homed machine position.
|
||||
|
||||
// Set pull-off motion target. Seperated from above loop if target is dependent on sys.position.
|
||||
if (settings.homing_pulloff > 0.0) {
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
if (cycle_mask & bit(idx)) {
|
||||
#ifdef HOMING_FORCE_SET_ORIGIN
|
||||
target[idx] = settings.homing_pulloff;
|
||||
if ( bit_isfalse(settings.homing_dir_mask,bit(idx)) ) { target[idx] = -target[idx]; }
|
||||
#else
|
||||
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
|
||||
target[idx] = settings.homing_pulloff+settings.max_travel[idx];
|
||||
} else {
|
||||
target[idx] = -settings.homing_pulloff;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// Non-active cycle axis. Set target to not move during pull-off.
|
||||
target[idx] = system_convert_axis_steps_to_mpos(sys.position, idx);
|
||||
}
|
||||
}
|
||||
#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.rt_exec_state, EXEC_CYCLE_START);
|
||||
protocol_execute_realtime();
|
||||
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,
|
||||
// the workspace volume is in all negative space, and the system is in normal operation.
|
||||
void limits_soft_check(float *target)
|
||||
{
|
||||
uint8_t idx;
|
||||
uint8_t soft_limit_error = false;
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
|
||||
#ifdef HOMING_FORCE_SET_ORIGIN
|
||||
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
|
||||
// NOTE: max_travel is stored as negative
|
||||
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
|
||||
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { soft_limit_error = true; }
|
||||
} else {
|
||||
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
|
||||
}
|
||||
#else
|
||||
// NOTE: max_travel is stored as negative
|
||||
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; }
|
||||
#endif
|
||||
|
||||
if (soft_limit_error) {
|
||||
// Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
|
||||
// workspace volume so just come to a controlled stop so position is not lost. When complete
|
||||
// enter alarm mode.
|
||||
if (sys.state == STATE_CYCLE) {
|
||||
bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD);
|
||||
do {
|
||||
protocol_execute_realtime();
|
||||
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.rt_exec_alarm, (EXEC_ALARM_SOFT_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate soft limit critical event
|
||||
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
41
grbl/limits.h
Normal file
41
grbl/limits.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
limits.h - code pertaining to limit-switches and performing the homing cycle
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
*/
|
||||
|
||||
#ifndef limits_h
|
||||
#define limits_h
|
||||
|
||||
|
||||
// Initialize the limits module
|
||||
void limits_init();
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
90
grbl/main.c
Normal file
90
grbl/main.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
main.c - An embedded CNC Controller with rs274/ngc (g-code) support
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Declare system global variable structure
|
||||
system_t sys;
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Initialize system upon power-up.
|
||||
serial_init(); // Setup serial baud rate and interrupts
|
||||
settings_init(); // Load Grbl settings from EEPROM
|
||||
stepper_init(); // Configure stepper pins and interrupt timers
|
||||
system_init(); // Configure pinout pins and pin-change interrupt
|
||||
|
||||
memset(&sys, 0, sizeof(sys)); // Clear all system variables
|
||||
sys.abort = true; // Set abort to complete initialization
|
||||
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.
|
||||
|
||||
// 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.
|
||||
plan_sync_position();
|
||||
gc_sync_position();
|
||||
|
||||
// Reset system variables.
|
||||
sys.abort = false;
|
||||
sys.rt_exec_state = 0;
|
||||
sys.rt_exec_alarm = 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 */
|
||||
}
|
376
grbl/motion_control.c
Normal file
376
grbl/motion_control.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
motion_control.c - high level interface for issuing motion commands
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
Copyright (c) 2011 Jens Geisler
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
|
||||
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
|
||||
// (1 minute)/feed_rate time.
|
||||
// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
|
||||
// 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.
|
||||
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { limits_soft_check(target); }
|
||||
|
||||
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
|
||||
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
|
||||
// to insert a backlash line motion(s) before the intended line motion and will require its own
|
||||
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
|
||||
// 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_realtime(); // Check for any run-time commands
|
||||
if (sys.abort) { return; } // Bail, if system abort.
|
||||
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
|
||||
else { break; }
|
||||
} while (1);
|
||||
|
||||
// Plan and queue motion into planner buffer
|
||||
#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; }
|
||||
}
|
||||
|
||||
|
||||
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
|
||||
// offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is
|
||||
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
|
||||
// for vector transformation direction.
|
||||
// The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance
|
||||
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal
|
||||
// distance from segment to the circle when the end points both lie on the circle.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
|
||||
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc, int32_t line_number)
|
||||
#else
|
||||
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
|
||||
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc)
|
||||
#endif
|
||||
{
|
||||
float center_axis0 = position[axis_0] + offset[axis_0];
|
||||
float center_axis1 = position[axis_1] + offset[axis_1];
|
||||
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;
|
||||
float rt_axis1 = target[axis_1] - center_axis1;
|
||||
|
||||
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
|
||||
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
|
||||
if (is_clockwise_arc) { // Correct atan2 output per direction
|
||||
if (angular_travel >= 0) { angular_travel -= 2*M_PI; }
|
||||
} else {
|
||||
if (angular_travel <= 0) { angular_travel += 2*M_PI; }
|
||||
}
|
||||
|
||||
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
|
||||
// (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.
|
||||
// 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
|
||||
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
|
||||
// all segments.
|
||||
if (invert_feed_rate) { feed_rate *= segments; }
|
||||
|
||||
float theta_per_segment = angular_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);
|
||||
sin(phi) cos(phi] * r ;
|
||||
|
||||
For arc generation, the center of the circle is the axis of rotation and the radius vector is
|
||||
defined from the circle center to the initial position. Each line segment is formed by successive
|
||||
vector rotations. Single precision values can accumulate error greater than tool precision in rare
|
||||
cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very
|
||||
expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute.
|
||||
|
||||
Small angle approximation may be used to reduce computation overhead further. A third-order approximation
|
||||
(second order sin() has too much error) holds for most, if not, all CNC applications. Note that this
|
||||
approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than
|
||||
~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This
|
||||
scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated
|
||||
and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC
|
||||
applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a
|
||||
low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate.
|
||||
|
||||
This approximation also allows mc_arc to immediately insert a line segment into the planner
|
||||
without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
|
||||
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
|
||||
This is important when there are successive arc motions.
|
||||
*/
|
||||
// Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec
|
||||
float cos_T = 2.0 - theta_per_segment*theta_per_segment;
|
||||
float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0);
|
||||
cos_T *= 0.5;
|
||||
|
||||
float sin_Ti;
|
||||
float cos_Ti;
|
||||
float r_axisi;
|
||||
uint16_t i;
|
||||
uint8_t count = 0;
|
||||
|
||||
for (i = 1; i<segments; i++) { // Increment (segments-1).
|
||||
|
||||
if (count < N_ARC_CORRECTION) {
|
||||
// Apply vector rotation matrix. ~40 usec
|
||||
r_axisi = r_axis0*sin_T + r_axis1*cos_T;
|
||||
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
|
||||
r_axis1 = r_axisi;
|
||||
count++;
|
||||
} else {
|
||||
// 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);
|
||||
sin_Ti = sin(i*theta_per_segment);
|
||||
r_axis0 = -offset[axis_0]*cos_Ti + offset[axis_1]*sin_Ti;
|
||||
r_axis1 = -offset[axis_0]*sin_Ti - offset[axis_1]*cos_Ti;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Update arc_target location
|
||||
position[axis_0] = center_axis0 + r_axis0;
|
||||
position[axis_1] = center_axis1 + r_axis1;
|
||||
position[axis_linear] += linear_per_segment;
|
||||
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
mc_line(position, feed_rate, invert_feed_rate, line_number);
|
||||
#else
|
||||
mc_line(position, feed_rate, invert_feed_rate);
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
protocol_buffer_synchronize();
|
||||
delay_ms(floor(1000*seconds-i*DWELL_TIME_STEP)); // Delay millisecond remainder.
|
||||
while (i-- > 0) {
|
||||
// NOTE: Check and execute realtime commands during dwell every <= DWELL_TIME_STEP milliseconds.
|
||||
protocol_execute_realtime();
|
||||
if (sys.abort) { return; }
|
||||
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
|
||||
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
|
||||
// executing the homing cycle. This prevents incorrect buffered plans after homing.
|
||||
void mc_homing_cycle()
|
||||
{
|
||||
// Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems
|
||||
// with machines with limits wired on both ends of travel to one limit pin.
|
||||
// TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function.
|
||||
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
|
||||
uint8_t limit_state = (LIMIT_PIN & LIMIT_MASK);
|
||||
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { limit_state ^= LIMIT_MASK; }
|
||||
if (limit_state) {
|
||||
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
|
||||
bit_true_atomic(sys.rt_exec_alarm, (EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
limits_disable(); // Disable hard limits pin change register for cycle duration
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Perform homing routine. NOTE: Special motion case. Only system reset works.
|
||||
|
||||
// Search to engage all axes limit switches at faster homing seek rate.
|
||||
limits_go_home(HOMING_CYCLE_0); // Homing cycle 0
|
||||
#ifdef HOMING_CYCLE_1
|
||||
limits_go_home(HOMING_CYCLE_1); // Homing cycle 1
|
||||
#endif
|
||||
#ifdef HOMING_CYCLE_2
|
||||
limits_go_home(HOMING_CYCLE_2); // Homing cycle 2
|
||||
#endif
|
||||
|
||||
protocol_execute_realtime(); // Check for reset and set system abort.
|
||||
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
|
||||
|
||||
// Homing cycle complete! Setup system for normal operation.
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
// Gcode parser position was circumvented by the limits_go_home() routine, so sync position now.
|
||||
gc_sync_position();
|
||||
|
||||
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
|
||||
limits_init();
|
||||
}
|
||||
|
||||
|
||||
// Perform tool length probe cycle. Requires probe switch.
|
||||
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
|
||||
uint8_t is_no_error, int32_t line_number)
|
||||
#else
|
||||
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
|
||||
uint8_t is_no_error)
|
||||
#endif
|
||||
{
|
||||
// TODO: Need to update this cycle so it obeys a non-auto cycle start.
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
|
||||
// Finish all queued commands and empty planner buffer before starting probe cycle.
|
||||
protocol_buffer_synchronize();
|
||||
uint8_t auto_start_state = sys.auto_start; // Store run state
|
||||
|
||||
// Initialize probing control variables
|
||||
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
|
||||
probe_configure_invert_mask(is_probe_away);
|
||||
|
||||
// After syncing, check if probe is already triggered. If so, halt and issue alarm.
|
||||
// NOTE: This probe initialization error applies to all probing cycles.
|
||||
if ( probe_get_state() ) { // Check probe pin state.
|
||||
bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_PROBE_FAIL);
|
||||
protocol_execute_realtime();
|
||||
}
|
||||
if (sys.abort) { return; } // Return if system reset has been issued.
|
||||
|
||||
// Setup and queue probing motion. Auto cycle-start should not start the cycle.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
mc_line(target, feed_rate, invert_feed_rate, line_number);
|
||||
#else
|
||||
mc_line(target, feed_rate, invert_feed_rate);
|
||||
#endif
|
||||
|
||||
// Activate the probing state monitor in the stepper module.
|
||||
sys.probe_state = PROBE_ACTIVE;
|
||||
|
||||
// Perform probing cycle. Wait here until probe is triggered or motion completes.
|
||||
bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START);
|
||||
do {
|
||||
protocol_execute_realtime();
|
||||
if (sys.abort) { return; } // Check for system abort
|
||||
} while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED));
|
||||
|
||||
// Probing cycle complete!
|
||||
|
||||
// Set state variables and error out, if the probe failed and cycle with error is enabled.
|
||||
if (sys.probe_state == PROBE_ACTIVE) {
|
||||
if (is_no_error) { memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); }
|
||||
else { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_PROBE_FAIL); }
|
||||
} else {
|
||||
sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully.
|
||||
}
|
||||
sys.probe_state = PROBE_OFF; // Ensure probe state monitor is disabled.
|
||||
protocol_execute_realtime(); // Check and execute run-time commands
|
||||
if (sys.abort) { return; } // Check for system abort
|
||||
|
||||
// Reset the stepper and planner buffers to remove the remainder of the probe motion.
|
||||
st_reset(); // Reest step segment buffer.
|
||||
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared.
|
||||
plan_sync_position(); // Sync planner position to current machine position.
|
||||
|
||||
// TODO: Update the g-code parser code to not require this target calculation but uses a gc_sync_position() call.
|
||||
// NOTE: The target[] variable updated here will be sent back and synced with the g-code parser.
|
||||
system_convert_array_steps_to_mpos(target, sys.position);
|
||||
|
||||
// Restore run state before returning
|
||||
sys.auto_start = auto_start_state;
|
||||
|
||||
#ifdef MESSAGE_PROBE_COORDINATES
|
||||
// All done! Output the probe position as message.
|
||||
report_probe_parameters();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Method to ready the system to reset by setting the realtime reset command and killing any
|
||||
// active processes in the system. This also checks if a system reset is issued while Grbl
|
||||
// is in a motion state. If so, kills the steppers and sets the system alarm to flag position
|
||||
// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by
|
||||
// realtime abort command and hard limits. So, keep to a minimum.
|
||||
void mc_reset()
|
||||
{
|
||||
// Only this function can set the system reset. Helps prevent multiple kill calls.
|
||||
if (bit_isfalse(sys.rt_exec_state, EXEC_RESET)) {
|
||||
bit_true_atomic(sys.rt_exec_state, EXEC_RESET);
|
||||
|
||||
// Kill spindle and coolant.
|
||||
spindle_stop();
|
||||
coolant_stop();
|
||||
|
||||
// Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging
|
||||
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
|
||||
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
|
||||
// violated, by which, all bets are off.
|
||||
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) {
|
||||
bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE);
|
||||
st_go_idle(); // Force kill steppers. Position has likely been lost.
|
||||
}
|
||||
}
|
||||
}
|
72
grbl/motion_control.h
Normal file
72
grbl/motion_control.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
motion_control.h - high level interface for issuing motion commands
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef motion_control_h
|
||||
#define motion_control_h
|
||||
|
||||
|
||||
#define HOMING_CYCLE_LINE_NUMBER -1
|
||||
|
||||
// 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, is_clockwise_arc boolean. Used
|
||||
// for vector transformation direction.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
|
||||
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc, int32_t line_number);
|
||||
#else
|
||||
void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
|
||||
uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc);
|
||||
#endif
|
||||
|
||||
// Dwell for a specific number of seconds
|
||||
void mc_dwell(float seconds);
|
||||
|
||||
// Perform homing cycle to locate machine zero. Requires limit switches.
|
||||
void mc_homing_cycle();
|
||||
|
||||
// Perform tool length probe cycle. Requires probe switch.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
|
||||
uint8_t is_no_error, int32_t line_number);
|
||||
#else
|
||||
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away,
|
||||
uint8_t is_no_error);
|
||||
#endif
|
||||
|
||||
// Performs system reset. If in motion state, kills all motion and sets system alarm.
|
||||
void mc_reset();
|
||||
|
||||
#endif
|
148
grbl/nuts_bolts.c
Normal file
148
grbl/nuts_bolts.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
nuts_bolts.c - Shared functions
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
|
||||
|
||||
|
||||
// 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
|
||||
// available conversion method examples, but has been highly optimized for Grbl. For known
|
||||
// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
|
||||
// Scientific notation is officially not supported by g-code, and the 'E' character may
|
||||
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
|
||||
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
|
||||
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
|
||||
{
|
||||
char *ptr = line + *char_counter;
|
||||
unsigned char c;
|
||||
|
||||
// Grab first character and increment pointer. No spaces assumed in line.
|
||||
c = *ptr++;
|
||||
|
||||
// Capture initial positive/minus character
|
||||
bool isnegative = false;
|
||||
if (c == '-') {
|
||||
isnegative = true;
|
||||
c = *ptr++;
|
||||
} else if (c == '+') {
|
||||
c = *ptr++;
|
||||
}
|
||||
|
||||
// Extract number into fast integer. Track decimal in terms of exponent value.
|
||||
uint32_t intval = 0;
|
||||
int8_t exp = 0;
|
||||
uint8_t ndigit = 0;
|
||||
bool isdecimal = false;
|
||||
while(1) {
|
||||
c -= '0';
|
||||
if (c <= 9) {
|
||||
ndigit++;
|
||||
if (ndigit <= MAX_INT_DIGITS) {
|
||||
if (isdecimal) { exp--; }
|
||||
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
|
||||
} else {
|
||||
if (!(isdecimal)) { exp++; } // Drop overflow digits
|
||||
}
|
||||
} else if (c == (('.'-'0') & 0xff) && !(isdecimal)) {
|
||||
isdecimal = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
c = *ptr++;
|
||||
}
|
||||
|
||||
// Return if no digits have been read.
|
||||
if (!ndigit) { return(false); };
|
||||
|
||||
// Convert integer into floating point.
|
||||
float fval;
|
||||
fval = (float)intval;
|
||||
|
||||
// Apply decimal. Should perform no more than two floating point multiplications for the
|
||||
// expected range of E0 to E-4.
|
||||
if (fval != 0) {
|
||||
while (exp <= -2) {
|
||||
fval *= 0.01;
|
||||
exp += 2;
|
||||
}
|
||||
if (exp < 0) {
|
||||
fval *= 0.1;
|
||||
} else if (exp > 0) {
|
||||
do {
|
||||
fval *= 10.0;
|
||||
} while (--exp > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign floating point value with correct sign.
|
||||
if (isnegative) {
|
||||
*float_ptr = -fval;
|
||||
} else {
|
||||
*float_ptr = fval;
|
||||
}
|
||||
|
||||
*char_counter = ptr - line - 1; // Set char_counter to next statement
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(),
|
||||
// which only accepts constants in future compiler releases.
|
||||
void delay_ms(uint16_t ms)
|
||||
{
|
||||
while ( ms-- ) { _delay_ms(1); }
|
||||
}
|
||||
|
||||
|
||||
// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(),
|
||||
// which only accepts constants in future compiler releases. Written to perform more
|
||||
// efficiently with larger delays, as the counter adds parasitic time in each iteration.
|
||||
void delay_us(uint32_t us)
|
||||
{
|
||||
while (us) {
|
||||
if (us < 10) {
|
||||
_delay_us(1);
|
||||
us--;
|
||||
} else if (us < 100) {
|
||||
_delay_us(10);
|
||||
us -= 10;
|
||||
} else if (us < 1000) {
|
||||
_delay_us(100);
|
||||
us -= 100;
|
||||
} else {
|
||||
_delay_ms(1);
|
||||
us -= 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Simple hypotenuse computation function.
|
||||
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); }
|
83
grbl/nuts_bolts.h
Normal file
83
grbl/nuts_bolts.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
nuts_bolts.h - Header file for shared definitions, variables, and functions
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef nuts_bolts_h
|
||||
#define nuts_bolts_h
|
||||
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
// Axis array index values. Must start with 0 and be continuous.
|
||||
#define N_AXIS 3 // Number of axes
|
||||
#define X_AXIS 0 // Axis indexing value.
|
||||
#define Y_AXIS 1
|
||||
#define Z_AXIS 2
|
||||
// #define A_AXIS 3
|
||||
|
||||
// CoreXY motor assignments. DO NOT ALTER.
|
||||
// NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations.
|
||||
#ifdef COREXY
|
||||
#define A_MOTOR X_AXIS // Must be X_AXIS
|
||||
#define B_MOTOR Y_AXIS // Must be Y_AXIS
|
||||
#endif
|
||||
|
||||
// Conversions
|
||||
#define MM_PER_INCH (25.40)
|
||||
#define INCH_PER_MM (0.0393701)
|
||||
#define TICKS_PER_MICROSECOND (F_CPU/1000000)
|
||||
|
||||
// Useful macros
|
||||
#define clear_vector(a) memset(a, 0, sizeof(a))
|
||||
#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)
|
||||
// #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS)
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
// Bit field and masking macros
|
||||
#define bit(n) (1 << n)
|
||||
#define bit_true_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (mask); SREG = sreg; }
|
||||
#define bit_false_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) &= ~(mask); SREG = sreg; }
|
||||
#define bit_toggle_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) ^= (mask); SREG = sreg; }
|
||||
#define bit_true(x,mask) (x) |= (mask)
|
||||
#define bit_false(x,mask) (x) &= ~(mask)
|
||||
#define bit_istrue(x,mask) ((x & mask) != 0)
|
||||
#define bit_isfalse(x,mask) ((x & mask) == 0)
|
||||
|
||||
// Read a floating point value from a string. Line points to the input buffer, char_counter
|
||||
// is the indexer pointing to the current character of the line, while float_ptr is
|
||||
// a pointer to the result variable. Returns true when it succeeds
|
||||
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);
|
||||
|
||||
// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us().
|
||||
void delay_us(uint32_t us);
|
||||
|
||||
// Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking.
|
||||
float hypot_f(float x, float y);
|
||||
|
||||
#endif
|
462
grbl/planner.c
Normal file
462
grbl/planner.c
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
planner.c - buffers movement commands and manages the acceleration profile plan
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
Copyright (c) 2011 Jens Geisler
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
#define SOME_LARGE_VALUE 1.0E+38 // Used by rapids and acceleration maximization calculations. Just needs
|
||||
// to be larger than any feasible (mm/min)^2 or mm/sec^2 value.
|
||||
|
||||
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
||||
static uint8_t block_buffer_tail; // Index of the block to process now
|
||||
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 {
|
||||
int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate
|
||||
// from g-code position for movements requiring multiple line motions,
|
||||
// i.e. arcs, canned cycles, and backlash compensation.
|
||||
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
|
||||
float previous_nominal_speed_sqr; // Nominal speed of previous path line segment
|
||||
} planner_t;
|
||||
static planner_t pl;
|
||||
|
||||
|
||||
// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
|
||||
uint8_t plan_next_block_index(uint8_t block_index)
|
||||
{
|
||||
block_index++;
|
||||
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
|
||||
return(block_index);
|
||||
}
|
||||
|
||||
|
||||
// Returns the index of the previous block in the ring buffer
|
||||
static uint8_t plan_prev_block_index(uint8_t block_index)
|
||||
{
|
||||
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
|
||||
block_index--;
|
||||
return(block_index);
|
||||
}
|
||||
|
||||
|
||||
/* PLANNER SPEED DEFINITION
|
||||
+--------+ <- current->nominal_speed
|
||||
/ \
|
||||
current->entry_speed -> + \
|
||||
| + <- next->entry_speed (aka exit speed)
|
||||
+-------------+
|
||||
time -->
|
||||
|
||||
Recalculates the motion plan according to the following basic guidelines:
|
||||
|
||||
1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
|
||||
(i.e. current->entry_speed) such that:
|
||||
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
|
||||
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 exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
|
||||
acceleration over the block travel distance.
|
||||
|
||||
When these stages are complete, the planner will have maximized the velocity profiles throughout the all
|
||||
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
static void planner_recalculate()
|
||||
{
|
||||
// Initialize block index to the last block in the planner buffer.
|
||||
uint8_t block_index = plan_prev_block_index(block_buffer_head);
|
||||
|
||||
// Bail. Can't do anything with one only one plan-able block.
|
||||
if (block_index == block_buffer_planned) { return; }
|
||||
|
||||
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
|
||||
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
|
||||
// NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
|
||||
float entry_speed_sqr;
|
||||
plan_block_t *next;
|
||||
plan_block_t *current = &block_buffer[block_index];
|
||||
|
||||
// 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);
|
||||
|
||||
// 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(); }
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
||||
// Any block set at its maximum entry speed also creates an optimal plan up to this
|
||||
// point in the buffer. When the plan is bracketed by either the beginning of the
|
||||
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
|
||||
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
|
||||
if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
|
||||
block_index = plan_next_block_index( block_index );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void plan_reset()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void plan_discard_current_block()
|
||||
{
|
||||
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
|
||||
uint8_t block_index = plan_next_block_index( block_buffer_tail );
|
||||
// Push block_buffer_planned pointer, if encountered.
|
||||
if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
|
||||
block_buffer_tail = block_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plan_block_t *plan_get_current_block()
|
||||
{
|
||||
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
|
||||
return(&block_buffer[block_buffer_tail]);
|
||||
}
|
||||
|
||||
|
||||
float plan_get_exec_block_exit_speed()
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (block_buffer_tail == next_buffer_head) { return(true); }
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
{
|
||||
// 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
|
||||
|
||||
// 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;
|
||||
#ifdef COREXY
|
||||
target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
|
||||
target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
|
||||
block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-pl.position[X_AXIS]) - (target_steps[Y_AXIS]-pl.position[Y_AXIS]));
|
||||
block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-pl.position[X_AXIS]) + (target_steps[Y_AXIS]-pl.position[Y_AXIS]));
|
||||
#endif
|
||||
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
// Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
|
||||
// Also, compute individual axes distance for move and prep unit vector calculations.
|
||||
// NOTE: Computes true distance from converted step values.
|
||||
#ifdef COREXY
|
||||
if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
|
||||
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
|
||||
block->steps[idx] = labs(target_steps[idx]-pl.position[idx]);
|
||||
}
|
||||
block->step_event_count = max(block->step_event_count, block->steps[idx]);
|
||||
if (idx == A_MOTOR) {
|
||||
delta_mm = ((target_steps[X_AXIS]-pl.position[X_AXIS]) - (target_steps[Y_AXIS]-pl.position[Y_AXIS]))/settings.steps_per_mm[idx];
|
||||
} else if (idx == B_MOTOR) {
|
||||
delta_mm = ((target_steps[X_AXIS]-pl.position[X_AXIS]) + (target_steps[Y_AXIS]-pl.position[Y_AXIS]))/settings.steps_per_mm[idx];
|
||||
} else {
|
||||
delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx];
|
||||
}
|
||||
#else
|
||||
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
|
||||
block->steps[idx] = labs(target_steps[idx]-pl.position[idx]);
|
||||
block->step_event_count = max(block->step_event_count, block->steps[idx]);
|
||||
delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx];
|
||||
#endif
|
||||
unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later.
|
||||
|
||||
// Set direction bits. Bit enabled always means direction is negative.
|
||||
if (delta_mm < 0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
|
||||
|
||||
// Incrementally compute total move distance by Euclidean norm. First add square of each term.
|
||||
block->millimeters += delta_mm*delta_mm;
|
||||
}
|
||||
block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt()
|
||||
|
||||
// Bail if this is a zero-length block. Highly unlikely to occur.
|
||||
if (block->step_event_count == 0) { return; }
|
||||
|
||||
// Adjust feed_rate value to mm/min depending on type of rate input (normal, inverse time, or rapids)
|
||||
// TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort.
|
||||
if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later
|
||||
else if (invert_feed_rate) { feed_rate = block->millimeters/feed_rate; }
|
||||
if (feed_rate < MINIMUM_FEED_RATE) { feed_rate = MINIMUM_FEED_RATE; } // Prevents step generation round-off condition.
|
||||
|
||||
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
|
||||
// down such that no individual axes maximum values are exceeded with respect to the line direction.
|
||||
// NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
|
||||
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
|
||||
float inverse_unit_vec_value;
|
||||
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides
|
||||
float junction_cos_theta = 0;
|
||||
for (idx=0; 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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
|
||||
mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
|
||||
stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
|
||||
is exactly the same. Instead of motioning all the way to junction point, the machine will
|
||||
just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
|
||||
a continuous mode path, but ARM-based microcontrollers most certainly do.
|
||||
|
||||
NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
|
||||
changed dynamically during operation nor can the line move geometry. This must be kept in
|
||||
memory in the event of a feedrate override changing the nominal speeds of blocks, which can
|
||||
change the overall maximum entry speed conditions of all blocks.
|
||||
*/
|
||||
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
|
||||
if (junction_cos_theta > 0.99) {
|
||||
// For a 0 degree acute junction, just set minimum junction speed.
|
||||
block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
|
||||
} else {
|
||||
junction_cos_theta = max(junction_cos_theta,-0.99); // Check for numerical round-off to avoid divide by zero.
|
||||
float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
|
||||
|
||||
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the
|
||||
// two junctions. However, this shouldn't be a significant problem except in extreme circumstances.
|
||||
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
|
||||
(block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
|
||||
|
||||
// New block is all set. Update buffer head and next buffer head indices.
|
||||
block_buffer_head = next_buffer_head;
|
||||
next_buffer_head = plan_next_block_index(block_buffer_head);
|
||||
|
||||
// Finish up by recalculating the plan with the new block.
|
||||
planner_recalculate();
|
||||
}
|
||||
|
||||
|
||||
// Reset the planner position vectors. Called by the system abort/initialization routine.
|
||||
void plan_sync_position()
|
||||
{
|
||||
// TODO: For motor configurations not in the same coordinate frame as the machine position,
|
||||
// this function needs to be updated to accomodate the difference.
|
||||
uint8_t idx;
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
#ifdef COREXY
|
||||
if (idx==A_MOTOR) {
|
||||
pl.position[idx] = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2;
|
||||
} else if (idx==B_MOTOR) {
|
||||
pl.position[idx] = (sys.position[A_MOTOR] - sys.position[B_MOTOR])/2;
|
||||
} else {
|
||||
pl.position[idx] = sys.position[idx];
|
||||
}
|
||||
#else
|
||||
pl.position[idx] = sys.position[idx];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of active blocks are in the planner buffer.
|
||||
uint8_t plan_get_block_buffer_count()
|
||||
{
|
||||
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
|
||||
return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head));
|
||||
}
|
||||
|
||||
|
||||
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
|
||||
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
|
||||
void plan_cycle_reinitialize()
|
||||
{
|
||||
// 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();
|
||||
}
|
102
grbl/planner.h
Normal file
102
grbl/planner.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
planner.h - buffers movement commands and manages the acceleration profile plan
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef planner_h
|
||||
#define planner_h
|
||||
|
||||
|
||||
// The number of linear motions that can be in the plan at any give time
|
||||
#ifndef BLOCK_BUFFER_SIZE
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
#define BLOCK_BUFFER_SIZE 16
|
||||
#else
|
||||
#define BLOCK_BUFFER_SIZE 18
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
|
||||
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
|
||||
uint32_t steps[N_AXIS]; // Step count along each axis
|
||||
uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
|
||||
|
||||
// Fields used by the motion planner to manage acceleration
|
||||
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
|
||||
float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
|
||||
// neighboring nominal speeds with overrides in (mm/min)^2
|
||||
float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
|
||||
float nominal_speed_sqr; // Axis-limit adjusted nominal speed for this block in (mm/min)^2
|
||||
float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2)
|
||||
float millimeters; // The remaining distance for this block to be executed in (mm)
|
||||
// uint8_t max_override; // Maximum override value based on axis speed limits
|
||||
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
int32_t line_number;
|
||||
#endif
|
||||
} plan_block_t;
|
||||
|
||||
|
||||
// Initialize and reset the motion plan subsystem
|
||||
void plan_reset();
|
||||
|
||||
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
|
||||
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
||||
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number);
|
||||
#else
|
||||
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate);
|
||||
#endif
|
||||
|
||||
// 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
|
||||
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_sync_position();
|
||||
|
||||
// Reinitialize plan with a partially completed block
|
||||
void plan_cycle_reinitialize();
|
||||
|
||||
// Returns the number of active blocks are in the planner buffer.
|
||||
uint8_t plan_get_block_buffer_count();
|
||||
|
||||
// Returns the status of the block ring buffer. True, if buffer is full.
|
||||
uint8_t plan_check_full_buffer();
|
||||
|
||||
#endif
|
210
grbl/print.c
Normal file
210
grbl/print.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
print.c - Functions for formatting output strings
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
void printString(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
serial_write(*s++);
|
||||
}
|
||||
|
||||
|
||||
// Print a string stored in PGM-memory
|
||||
void printPgmString(const char *s)
|
||||
{
|
||||
char c;
|
||||
while ((c = pgm_read_byte_near(s++)))
|
||||
serial_write(c);
|
||||
}
|
||||
|
||||
|
||||
// void printIntegerInBase(unsigned long n, unsigned long base)
|
||||
// {
|
||||
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
|
||||
// unsigned long i = 0;
|
||||
//
|
||||
// if (n == 0) {
|
||||
// serial_write('0');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// while (n > 0) {
|
||||
// buf[i++] = n % base;
|
||||
// n /= base;
|
||||
// }
|
||||
//
|
||||
// for (; i > 0; i--)
|
||||
// serial_write(buf[i - 1] < 10 ?
|
||||
// '0' + buf[i - 1] :
|
||||
// 'A' + buf[i - 1] - 10);
|
||||
// }
|
||||
|
||||
|
||||
void print_uint8_base2(uint8_t n)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
uint8_t i = 0;
|
||||
|
||||
for (; i < 8; i++) {
|
||||
buf[i] = n & 1;
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
for (; i > 0; i--)
|
||||
serial_write('0' + buf[i - 1]);
|
||||
}
|
||||
|
||||
|
||||
void print_uint8_base10(uint8_t n)
|
||||
{
|
||||
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;
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
for (; i > 0; i--)
|
||||
serial_write('0' + buf[i-1]);
|
||||
}
|
||||
|
||||
|
||||
void printInteger(long n)
|
||||
{
|
||||
if (n < 0) {
|
||||
serial_write('-');
|
||||
print_uint32_base10((-n));
|
||||
} else {
|
||||
print_uint32_base10(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert float to string by immediately converting to a long integer, which contains
|
||||
// more digits than a float. Number of decimal places, which are tracked by a counter,
|
||||
// may be set by the user. The integer is then efficiently converted to a string.
|
||||
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
|
||||
// techniques are actually just slightly slower. Found this out the hard way.
|
||||
void printFloat(float n, uint8_t decimal_places)
|
||||
{
|
||||
if (n < 0) {
|
||||
serial_write('-');
|
||||
n = -n;
|
||||
}
|
||||
|
||||
uint8_t decimals = decimal_places;
|
||||
while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4.
|
||||
n *= 100;
|
||||
decimals -= 2;
|
||||
}
|
||||
if (decimals) { n *= 10; }
|
||||
n += 0.5; // Add rounding factor. Ensures carryover through entire value.
|
||||
|
||||
// Generate digits backwards and store in string.
|
||||
unsigned char buf[10];
|
||||
uint8_t i = 0;
|
||||
uint32_t a = (long)n;
|
||||
buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero.
|
||||
while(a > 0) {
|
||||
if (i == decimal_places) { i++; } // Skip decimal point location
|
||||
buf[i++] = (a % 10) + '0'; // Get digit
|
||||
a /= 10;
|
||||
}
|
||||
while (i < decimal_places) {
|
||||
buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1)
|
||||
}
|
||||
if (i == decimal_places) { // Fill in leading zero, if needed.
|
||||
i++;
|
||||
buf[i++] = '0';
|
||||
}
|
||||
|
||||
// Print the generated string.
|
||||
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); }
|
||||
|
||||
|
||||
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
|
||||
void printFreeMemory()
|
||||
{
|
||||
extern int __heap_start, *__brkval;
|
||||
uint16_t free; // Up to 64k values.
|
||||
free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
|
||||
printInteger((int32_t)free);
|
||||
printString(" ");
|
||||
}
|
58
grbl/print.h
Normal file
58
grbl/print.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
print.h - Functions for formatting output strings
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef print_h
|
||||
#define print_h
|
||||
|
||||
|
||||
void printString(const char *s);
|
||||
|
||||
void printPgmString(const char *s);
|
||||
|
||||
void printInteger(long n);
|
||||
|
||||
void print_uint32_base10(uint32_t n);
|
||||
|
||||
void print_uint8_base2(uint8_t 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);
|
||||
|
||||
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
|
||||
void printFreeMemory();
|
||||
|
||||
#endif
|
68
grbl/probe.c
Normal file
68
grbl/probe.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
probe.c - code pertaining to probing methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2014-2015 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 "grbl.h"
|
||||
|
||||
|
||||
// Inverts the probe pin state depending on user settings and probing cycle mode.
|
||||
uint8_t probe_invert_mask;
|
||||
|
||||
|
||||
// Probe pin initialization routine.
|
||||
void probe_init()
|
||||
{
|
||||
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
|
||||
#ifdef DISABLE_PROBE_PIN_PULL_UP
|
||||
PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down.
|
||||
#else
|
||||
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
|
||||
#endif
|
||||
// probe_configure_invert_mask(false); // Initialize invert mask. Not required. Updated when in-use.
|
||||
}
|
||||
|
||||
|
||||
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
|
||||
// appropriately set the pin logic according to setting for normal-high/normal-low operation
|
||||
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
|
||||
void probe_configure_invert_mask(uint8_t is_probe_away)
|
||||
{
|
||||
probe_invert_mask = 0; // Initialize as zero.
|
||||
if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; }
|
||||
if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; }
|
||||
}
|
||||
|
||||
|
||||
// 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) ^ probe_invert_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.rt_exec_state, EXEC_FEED_HOLD);
|
||||
}
|
||||
}
|
||||
}
|
43
grbl/probe.h
Normal file
43
grbl/probe.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
probe.h - code pertaining to probing methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2014-2015 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 // Probing disabled or not in use. (Must be zero.)
|
||||
#define PROBE_ACTIVE 1 // Actively watching the input pin.
|
||||
|
||||
// Probe pin initialization routine.
|
||||
void probe_init();
|
||||
|
||||
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
|
||||
// appropriately set the pin logic according to setting for normal-high/normal-low operation
|
||||
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
|
||||
void probe_configure_invert_mask(uint8_t is_probe_away);
|
||||
|
||||
// Returns probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
|
||||
uint8_t probe_get_state();
|
||||
|
||||
// Monitors probe pin state and records the system position when detected. Called by the
|
||||
// stepper ISR per ISR tick.
|
||||
void probe_state_monitor();
|
||||
|
||||
#endif
|
299
grbl/protocol.c
Normal file
299
grbl/protocol.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
protocol.c - controls Grbl execution protocol and procedures
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
|
||||
|
||||
|
||||
// Directs and executes one line of formatted input from protocol_process. While mostly
|
||||
// incoming streaming g-code blocks, this also directs and executes Grbl internal commands,
|
||||
// such as settings, initiating the homing cycle, and toggling switch states.
|
||||
static void protocol_execute_line(char *line)
|
||||
{
|
||||
protocol_execute_realtime(); // Runtime command check point.
|
||||
if (sys.abort) { return; } // Bail to calling function upon system abort
|
||||
|
||||
if (line[0] == 0) {
|
||||
// Empty or comment line. Send status message for syncing purposes.
|
||||
report_status_message(STATUS_OK);
|
||||
|
||||
} else if (line[0] == '$') {
|
||||
// Grbl '$' system command
|
||||
report_status_message(system_execute_line(line));
|
||||
|
||||
} else if (sys.state == STATE_ALARM) {
|
||||
// Everything else is gcode. Block if in alarm mode.
|
||||
report_status_message(STATUS_ALARM_LOCK);
|
||||
|
||||
} 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_realtime(); // Runtime command check point.
|
||||
if (sys.abort) { return; } // Bail to main() program loop to reset system.
|
||||
|
||||
}
|
||||
|
||||
return; /* Never reached */
|
||||
}
|
||||
|
||||
|
||||
// Executes run-time commands, when required. This is called from various check points in the main
|
||||
// program, primarily where there may be a while loop waiting for a buffer to clear space or any
|
||||
// point where the execution time from the last check point may be more than a fraction of a second.
|
||||
// This is a way to execute realtime commands asynchronously (aka multitasking) with grbl's g-code
|
||||
// parsing and planning functions. This function also serves as an interface for the interrupts to
|
||||
// set the system realtime flags, where only the main program handles them, removing the need to
|
||||
// define more computationally-expensive volatile variables. This also provides a controlled way to
|
||||
// execute certain tasks without having two or more instances of the same task, such as the planner
|
||||
// recalculating the buffer upon a feedhold or override.
|
||||
// NOTE: The sys.rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts,
|
||||
// limit switches, or the main program.
|
||||
void protocol_execute_realtime()
|
||||
{
|
||||
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
|
||||
|
||||
// Check and execute alarms.
|
||||
rt_exec = sys.rt_exec_alarm; // Copy volatile sys.rt_exec_alarm.
|
||||
if (rt_exec) { // Enter only if any bit flag is true
|
||||
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
|
||||
// the source of the error to the user. If critical, Grbl disables by entering an infinite
|
||||
// loop until system reset/abort.
|
||||
sys.state = STATE_ALARM; // Set system alarm state
|
||||
if (rt_exec & EXEC_ALARM_HARD_LIMIT) {
|
||||
report_alarm_message(ALARM_HARD_LIMIT_ERROR);
|
||||
} else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) {
|
||||
report_alarm_message(ALARM_SOFT_LIMIT_ERROR);
|
||||
} else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) {
|
||||
report_alarm_message(ALARM_ABORT_CYCLE);
|
||||
} else if (rt_exec & EXEC_ALARM_PROBE_FAIL) {
|
||||
report_alarm_message(ALARM_PROBE_FAIL);
|
||||
}
|
||||
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
|
||||
if (rt_exec & EXEC_CRITICAL_EVENT) {
|
||||
report_feedback_message(MESSAGE_CRITICAL_EVENT);
|
||||
bit_false_atomic(sys.rt_exec_state,EXEC_RESET); // Disable any existing reset
|
||||
do {
|
||||
// Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits
|
||||
// typically occur while unattended or not paying attention. Gives the user time
|
||||
// to do what is needed before resetting, like killing the incoming stream. The
|
||||
// same could be said about soft limits. While the position is not lost, the incoming
|
||||
// stream could be still engaged and cause a serious crash if it continues afterwards.
|
||||
} while (bit_isfalse(sys.rt_exec_state,EXEC_RESET));
|
||||
}
|
||||
bit_false_atomic(sys.rt_exec_alarm,0xFF); // Clear all alarm flags
|
||||
}
|
||||
|
||||
// Check amd execute realtime commands
|
||||
rt_exec = sys.rt_exec_state; // Copy volatile sys.rt_exec_state.
|
||||
if (rt_exec) { // Enter only if any bit flag is true
|
||||
// Execute system abort.
|
||||
if (rt_exec & EXEC_RESET) {
|
||||
sys.abort = true; // Only place this is set true.
|
||||
return; // Nothing else to do but exit.
|
||||
}
|
||||
|
||||
// Execute and serial print status
|
||||
if (rt_exec & EXEC_STATUS_REPORT) {
|
||||
report_realtime_status();
|
||||
bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
|
||||
}
|
||||
|
||||
// Execute a feed hold with deceleration, only during cycle.
|
||||
if (rt_exec & EXEC_FEED_HOLD) {
|
||||
// !!! During a cycle, the segment buffer has just been reloaded and full. So the math involved
|
||||
// with the feed hold should be fine for most, if not all, operational scenarios.
|
||||
if (sys.state == STATE_CYCLE) {
|
||||
sys.state = STATE_HOLD;
|
||||
st_update_plan_block_parameters();
|
||||
st_prep_buffer();
|
||||
sys.auto_start = false; // Disable planner auto start upon feed hold.
|
||||
}
|
||||
bit_false_atomic(sys.rt_exec_state,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.rt_exec_state,EXEC_CYCLE_START);
|
||||
}
|
||||
|
||||
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
|
||||
// realtime command execution in the main program, ensuring that the planner re-plans safely.
|
||||
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
|
||||
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
|
||||
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
|
||||
if (rt_exec & EXEC_CYCLE_STOP) {
|
||||
if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; }
|
||||
else { sys.state = STATE_IDLE; }
|
||||
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides flag byte (sys.override) and execution should be installed here, since they
|
||||
// are realtime and require a direct and controlled interface to the main stepper program.
|
||||
|
||||
// Reload step segment buffer
|
||||
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { st_prep_buffer(); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Block until all buffered steps are executed or in a cycle state. Works with feed hold
|
||||
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
||||
void protocol_buffer_synchronize()
|
||||
{
|
||||
// If system is queued, ensure cycle resumes if the auto start flag is present.
|
||||
protocol_auto_cycle_start();
|
||||
// Check and set auto start to resume cycle after synchronize and caller completes.
|
||||
if (sys.state == STATE_CYCLE) { sys.auto_start = true; }
|
||||
while (plan_get_current_block() || (sys.state == STATE_CYCLE)) {
|
||||
protocol_execute_realtime(); // 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.rt_exec_state, EXEC_CYCLE_START); } }
|
62
grbl/protocol.h
Normal file
62
grbl/protocol.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
protocol.h - controls Grbl execution protocol and procedures
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef protocol_h
|
||||
#define protocol_h
|
||||
|
||||
// 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 this
|
||||
// buffer.
|
||||
#ifndef LINE_BUFFER_SIZE
|
||||
#define LINE_BUFFER_SIZE 80
|
||||
#endif
|
||||
|
||||
// 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 realtime command at various stop points in main program
|
||||
void protocol_execute_realtime();
|
||||
|
||||
// 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
|
453
grbl/report.c
Normal file
453
grbl/report.c
Normal file
@ -0,0 +1,453 @@
|
||||
/*
|
||||
report.c - reporting and messaging methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file functions as the primary feedback interface for Grbl. Any outgoing data, such
|
||||
as the protocol status messages, feedback messages, and status reports, are stored here.
|
||||
For the most part, these functions primarily are called from protocol.c methods. If a
|
||||
different style feedback is desired (i.e. JSON), then a user can change these following
|
||||
methods to accomodate their needs.
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
|
||||
// For every incoming line, this method responds with an 'ok' for a successful command or an
|
||||
// 'error:' to indicate some error event with the line or some critical system error during
|
||||
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
|
||||
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
|
||||
// responses.
|
||||
// NOTE: In silent mode, all error codes are greater than zero.
|
||||
// TODO: Install silent mode to return only numeric values, primarily for GUIs.
|
||||
void report_status_message(uint8_t status_code)
|
||||
{
|
||||
if (status_code == 0) { // STATUS_OK
|
||||
printPgmString(PSTR("ok\r\n"));
|
||||
} else {
|
||||
printPgmString(PSTR("error: "));
|
||||
switch(status_code) {
|
||||
case STATUS_EXPECTED_COMMAND_LETTER:
|
||||
printPgmString(PSTR("Expected command letter")); break;
|
||||
case STATUS_BAD_NUMBER_FORMAT:
|
||||
printPgmString(PSTR("Bad number format")); break;
|
||||
case STATUS_INVALID_STATEMENT:
|
||||
printPgmString(PSTR("Invalid statement")); break;
|
||||
case STATUS_NEGATIVE_VALUE:
|
||||
printPgmString(PSTR("Value < 0")); break;
|
||||
case STATUS_SETTING_DISABLED:
|
||||
printPgmString(PSTR("Setting disabled")); break;
|
||||
case STATUS_SETTING_STEP_PULSE_MIN:
|
||||
printPgmString(PSTR("Value < 3 usec")); break;
|
||||
case STATUS_SETTING_READ_FAIL:
|
||||
printPgmString(PSTR("EEPROM read fail. Using defaults")); break;
|
||||
case STATUS_IDLE_ERROR:
|
||||
printPgmString(PSTR("Not idle")); break;
|
||||
case STATUS_ALARM_LOCK:
|
||||
printPgmString(PSTR("Alarm lock")); break;
|
||||
case STATUS_SOFT_LIMIT_ERROR:
|
||||
printPgmString(PSTR("Homing not enabled")); break;
|
||||
case STATUS_OVERFLOW:
|
||||
printPgmString(PSTR("Line overflow")); break;
|
||||
// case STATUS_MAX_STEP_RATE_EXCEEDED:
|
||||
// printPgmString(PSTR("Step rate > 30kHz")); break;
|
||||
|
||||
// Common g-code parser errors.
|
||||
case STATUS_GCODE_MODAL_GROUP_VIOLATION:
|
||||
printPgmString(PSTR("Modal group violation")); break;
|
||||
case STATUS_GCODE_UNSUPPORTED_COMMAND:
|
||||
printPgmString(PSTR("Unsupported command")); break;
|
||||
case STATUS_GCODE_UNDEFINED_FEED_RATE:
|
||||
printPgmString(PSTR("Undefined feed rate")); break;
|
||||
default:
|
||||
// Remaining g-code parser errors with error codes
|
||||
printPgmString(PSTR("Invalid gcode ID:"));
|
||||
print_uint8_base10(status_code); // Print error code for user reference
|
||||
}
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Prints alarm messages.
|
||||
void report_alarm_message(int8_t alarm_code)
|
||||
{
|
||||
printPgmString(PSTR("ALARM: "));
|
||||
switch (alarm_code) {
|
||||
case ALARM_HARD_LIMIT_ERROR:
|
||||
printPgmString(PSTR("Hard limit")); break;
|
||||
case ALARM_SOFT_LIMIT_ERROR:
|
||||
printPgmString(PSTR("Soft limit")); break;
|
||||
case ALARM_ABORT_CYCLE:
|
||||
printPgmString(PSTR("Abort during cycle")); break;
|
||||
case ALARM_PROBE_FAIL:
|
||||
printPgmString(PSTR("Probe fail")); break;
|
||||
}
|
||||
printPgmString(PSTR("\r\n"));
|
||||
delay_ms(500); // Force delay to ensure message clears serial write buffer.
|
||||
}
|
||||
|
||||
// Prints feedback messages. This serves as a centralized method to provide additional
|
||||
// user feedback for things that are not of the status/alarm message protocol. These are
|
||||
// messages such as setup warnings, switch toggling, and how to exit alarms.
|
||||
// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
|
||||
// is installed, the message number codes are less than zero.
|
||||
// TODO: Install silence feedback messages option in settings
|
||||
void report_feedback_message(uint8_t message_code)
|
||||
{
|
||||
printPgmString(PSTR("["));
|
||||
switch(message_code) {
|
||||
case MESSAGE_CRITICAL_EVENT:
|
||||
printPgmString(PSTR("Reset to continue")); break;
|
||||
case MESSAGE_ALARM_LOCK:
|
||||
printPgmString(PSTR("'$H'|'$X' to unlock")); break;
|
||||
case MESSAGE_ALARM_UNLOCK:
|
||||
printPgmString(PSTR("Caution: Unlocked")); break;
|
||||
case MESSAGE_ENABLED:
|
||||
printPgmString(PSTR("Enabled")); break;
|
||||
case MESSAGE_DISABLED:
|
||||
printPgmString(PSTR("Disabled")); break;
|
||||
}
|
||||
printPgmString(PSTR("]\r\n"));
|
||||
}
|
||||
|
||||
|
||||
// Welcome message
|
||||
void report_init_message()
|
||||
{
|
||||
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n"));
|
||||
}
|
||||
|
||||
// Grbl help message
|
||||
void report_grbl_help() {
|
||||
printPgmString(PSTR("$$ (view Grbl settings)\r\n"
|
||||
"$# (view # parameters)\r\n"
|
||||
"$G (view parser state)\r\n"
|
||||
"$I (view build info)\r\n"
|
||||
"$N (view startup blocks)\r\n"
|
||||
"$x=value (save Grbl setting)\r\n"
|
||||
"$Nx=line (save startup block)\r\n"
|
||||
"$C (check gcode mode)\r\n"
|
||||
"$X (kill alarm lock)\r\n"
|
||||
"$H (run homing cycle)\r\n"
|
||||
"~ (cycle start)\r\n"
|
||||
"! (feed hold)\r\n"
|
||||
"? (current status)\r\n"
|
||||
"ctrl-x (reset Grbl)\r\n"));
|
||||
}
|
||||
|
||||
|
||||
// Grbl global settings print out.
|
||||
// NOTE: The numbering scheme here must correlate to storing in settings.c
|
||||
void report_grbl_settings() {
|
||||
// Print Grbl settings.
|
||||
printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds);
|
||||
printPgmString(PSTR(" (step pulse, usec)\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time);
|
||||
printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask);
|
||||
printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
|
||||
printPgmString(PSTR(")\r\n$3=")); print_uint8_base10(settings.dir_invert_mask);
|
||||
printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask);
|
||||
printPgmString(PSTR(")\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
|
||||
printPgmString(PSTR(" (step enable invert, bool)\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
|
||||
printPgmString(PSTR(" (limit pins invert, bool)\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN));
|
||||
printPgmString(PSTR(" (probe pin invert, bool)\r\n$10=")); print_uint8_base10(settings.status_report_mask);
|
||||
printPgmString(PSTR(" (status report mask:")); print_uint8_base2(settings.status_report_mask);
|
||||
printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation);
|
||||
printPgmString(PSTR(" (junction deviation, mm)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance);
|
||||
printPgmString(PSTR(" (arc tolerance, mm)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
|
||||
printPgmString(PSTR(" (report inches, bool)\r\n$14=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
|
||||
printPgmString(PSTR(" (auto start, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
|
||||
printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
|
||||
printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
|
||||
printPgmString(PSTR(")\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate);
|
||||
printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate);
|
||||
printPgmString(PSTR(" (homing seek, mm/min)\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay);
|
||||
printPgmString(PSTR(" (homing debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
|
||||
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
|
||||
|
||||
// Print axis settings
|
||||
uint8_t idx, set_idx;
|
||||
uint8_t val = AXIS_SETTINGS_START_VAL;
|
||||
for (set_idx=0; set_idx<AXIS_N_SETTINGS; set_idx++) {
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
printPgmString(PSTR("$"));
|
||||
print_uint8_base10(val+idx);
|
||||
printPgmString(PSTR("="));
|
||||
switch (set_idx) {
|
||||
case 0: printFloat_SettingValue(settings.steps_per_mm[idx]); break;
|
||||
case 1: printFloat_SettingValue(settings.max_rate[idx]); break;
|
||||
case 2: printFloat_SettingValue(settings.acceleration[idx]/(60*60)); break;
|
||||
case 3: printFloat_SettingValue(-settings.max_travel[idx]); break;
|
||||
}
|
||||
printPgmString(PSTR(" ("));
|
||||
switch (idx) {
|
||||
case X_AXIS: printPgmString(PSTR("x")); break;
|
||||
case Y_AXIS: printPgmString(PSTR("y")); break;
|
||||
case Z_AXIS: printPgmString(PSTR("z")); break;
|
||||
}
|
||||
switch (set_idx) {
|
||||
case 0: printPgmString(PSTR(", step/mm")); break;
|
||||
case 1: printPgmString(PSTR(" max rate, mm/min")); break;
|
||||
case 2: printPgmString(PSTR(" accel, mm/sec^2")); break;
|
||||
case 3: printPgmString(PSTR(" max travel, mm")); break;
|
||||
}
|
||||
printPgmString(PSTR(")\r\n"));
|
||||
}
|
||||
val += AXIS_SETTINGS_INCREMENT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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] = system_convert_axis_steps_to_mpos(sys.probe_position,i);
|
||||
printFloat_CoordValue(print_position[i]);
|
||||
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
|
||||
}
|
||||
printPgmString(PSTR(":"));
|
||||
print_uint8_base10(sys.probe_succeeded);
|
||||
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;
|
||||
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
|
||||
if (!(settings_read_coord_data(coord_select,coord_data))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
return;
|
||||
}
|
||||
printPgmString(PSTR("[G"));
|
||||
switch (coord_select) {
|
||||
case 6: printPgmString(PSTR("28")); break;
|
||||
case 7: printPgmString(PSTR("30")); break;
|
||||
default: print_uint8_base10(coord_select+54); break; // G54-G59
|
||||
}
|
||||
printPgmString(PSTR(":"));
|
||||
for (i=0; i<N_AXIS; 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++) {
|
||||
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()
|
||||
{
|
||||
printPgmString(PSTR("["));
|
||||
|
||||
switch (gc_state.modal.motion) {
|
||||
case MOTION_MODE_SEEK : printPgmString(PSTR("G0")); break;
|
||||
case MOTION_MODE_LINEAR : printPgmString(PSTR("G1")); break;
|
||||
case MOTION_MODE_CW_ARC : printPgmString(PSTR("G2")); break;
|
||||
case MOTION_MODE_CCW_ARC : printPgmString(PSTR("G3")); break;
|
||||
case MOTION_MODE_PROBE_TOWARD : printPgmString(PSTR("G38.2")); break;
|
||||
case MOTION_MODE_PROBE_TOWARD_NO_ERROR : printPgmString(PSTR("G38.3")); break;
|
||||
case MOTION_MODE_PROBE_AWAY : printPgmString(PSTR("G38.4")); break;
|
||||
case MOTION_MODE_PROBE_AWAY_NO_ERROR : printPgmString(PSTR("G38.5")); break;
|
||||
case MOTION_MODE_NONE : printPgmString(PSTR("G80")); break;
|
||||
}
|
||||
|
||||
printPgmString(PSTR(" G"));
|
||||
print_uint8_base10(gc_state.modal.coord_select+54);
|
||||
|
||||
switch (gc_state.modal.plane_select) {
|
||||
case PLANE_SELECT_XY : printPgmString(PSTR(" G17")); break;
|
||||
case PLANE_SELECT_ZX : printPgmString(PSTR(" G18")); break;
|
||||
case PLANE_SELECT_YZ : printPgmString(PSTR(" G19")); break;
|
||||
}
|
||||
|
||||
if (gc_state.modal.units == UNITS_MODE_MM) { printPgmString(PSTR(" G21")); }
|
||||
else { printPgmString(PSTR(" G20")); }
|
||||
|
||||
if (gc_state.modal.distance == DISTANCE_MODE_ABSOLUTE) { printPgmString(PSTR(" G90")); }
|
||||
else { printPgmString(PSTR(" G91")); }
|
||||
|
||||
if (gc_state.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { printPgmString(PSTR(" G93")); }
|
||||
else { printPgmString(PSTR(" G94")); }
|
||||
|
||||
switch (gc_state.modal.program_flow) {
|
||||
case PROGRAM_FLOW_RUNNING : printPgmString(PSTR(" M0")); break;
|
||||
case PROGRAM_FLOW_PAUSED : printPgmString(PSTR(" M1")); break;
|
||||
case PROGRAM_FLOW_COMPLETED : printPgmString(PSTR(" M2")); 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_state.modal.coolant) {
|
||||
case COOLANT_DISABLE : printPgmString(PSTR(" M9")); break;
|
||||
case COOLANT_FLOOD_ENABLE : printPgmString(PSTR(" M8")); break;
|
||||
#ifdef ENABLE_M7
|
||||
case COOLANT_MIST_ENABLE : printPgmString(PSTR(" M7")); break;
|
||||
#endif
|
||||
}
|
||||
|
||||
printPgmString(PSTR(" T"));
|
||||
print_uint8_base10(gc_state.tool);
|
||||
|
||||
printPgmString(PSTR(" F"));
|
||||
printFloat_RateValue(gc_state.feed_rate);
|
||||
|
||||
#ifdef VARIABLE_SPINDLE
|
||||
printPgmString(PSTR(" S"));
|
||||
printFloat_RateValue(gc_state.spindle_speed);
|
||||
#endif
|
||||
|
||||
printPgmString(PSTR("]\r\n"));
|
||||
}
|
||||
|
||||
// Prints specified startup line
|
||||
void report_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
printPgmString(PSTR("$N")); print_uint8_base10(n);
|
||||
printPgmString(PSTR("=")); printString(line);
|
||||
printPgmString(PSTR("\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
|
||||
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
|
||||
// especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
|
||||
void report_realtime_status()
|
||||
{
|
||||
// **Under construction** Bare-bones status report. Provides real-time machine position relative to
|
||||
// the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually
|
||||
// to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask
|
||||
// for a user to select the desired real-time data.
|
||||
uint8_t i;
|
||||
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
|
||||
memcpy(current_position,sys.position,sizeof(sys.position));
|
||||
float print_position[N_AXIS];
|
||||
|
||||
// Report current machine state
|
||||
switch (sys.state) {
|
||||
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
|
||||
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
|
||||
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
|
||||
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
|
||||
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
|
||||
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
|
||||
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
|
||||
}
|
||||
|
||||
// If reporting a position, convert the current step count (current_position) to millimeters.
|
||||
if (bit_istrue(settings.status_report_mask,(BITFLAG_RT_STATUS_MACHINE_POSITION | BITFLAG_RT_STATUS_WORK_POSITION))) {
|
||||
system_convert_array_steps_to_mpos(print_position,current_position);
|
||||
}
|
||||
|
||||
// Report machine position
|
||||
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_MACHINE_POSITION)) {
|
||||
printPgmString(PSTR(",MPos:"));
|
||||
for (i=0; i< N_AXIS; i++) {
|
||||
printFloat_CoordValue(print_position[i]);
|
||||
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
|
||||
}
|
||||
}
|
||||
|
||||
// Report work position
|
||||
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_WORK_POSITION)) {
|
||||
printPgmString(PSTR(",WPos:"));
|
||||
for (i=0; i< N_AXIS; i++) {
|
||||
// Apply work coordinate offsets and tool length offset to current position.
|
||||
print_position[i] -= gc_state.coord_system[i]+gc_state.coord_offset[i];
|
||||
if (i == TOOL_LENGTH_OFFSET_AXIS) { print_position[i] -= gc_state.tool_length_offset; }
|
||||
printFloat_CoordValue(print_position[i]);
|
||||
if (i < (N_AXIS-1)) { printPgmString(PSTR(",")); }
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of active blocks are in the planner buffer.
|
||||
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_PLANNER_BUFFER)) {
|
||||
printPgmString(PSTR(",Buf:"));
|
||||
print_uint8_base10(plan_get_block_buffer_count());
|
||||
}
|
||||
|
||||
// Report serial read buffer status
|
||||
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_SERIAL_RX)) {
|
||||
printPgmString(PSTR(",RX:"));
|
||||
print_uint8_base10(serial_get_rx_buffer_count());
|
||||
}
|
||||
|
||||
#ifdef USE_LINE_NUMBERS
|
||||
// Report current line number
|
||||
printPgmString(PSTR(",Ln:"));
|
||||
int32_t ln=0;
|
||||
plan_block_t * pb = plan_get_current_block();
|
||||
if(pb != NULL) {
|
||||
ln = pb->line_number;
|
||||
}
|
||||
printInteger(ln);
|
||||
#endif
|
||||
|
||||
#ifdef REPORT_REALTIME_RATE
|
||||
// Report realtime rate
|
||||
printPgmString(PSTR(",F:"));
|
||||
printFloat_RateValue(st_get_realtime_rate());
|
||||
#endif
|
||||
|
||||
#ifdef REPORT_INPUT_PIN_STATES
|
||||
printPgmString(PSTR(",Lim:"));
|
||||
print_uint8_base2(LIMIT_PIN & LIMIT_MASK);
|
||||
printPgmString(PSTR(",Ctl:"));
|
||||
print_uint8_base2(CONTROL_PIN & CONTROL_MASK);
|
||||
#endif
|
||||
|
||||
printPgmString(PSTR(">\r\n"));
|
||||
}
|
106
grbl/report.h
Normal file
106
grbl/report.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
report.h - reporting and messaging methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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 report_h
|
||||
#define report_h
|
||||
|
||||
// Define Grbl status codes.
|
||||
#define STATUS_OK 0
|
||||
#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_MAX_STEP_RATE_EXCEEDED 12
|
||||
|
||||
#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_UNUSED_WORDS 36
|
||||
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37
|
||||
|
||||
// Define Grbl alarm codes. Less than zero to distinguish alarm error from status error.
|
||||
#define ALARM_HARD_LIMIT_ERROR -1
|
||||
#define ALARM_SOFT_LIMIT_ERROR -2
|
||||
#define ALARM_ABORT_CYCLE -3
|
||||
#define ALARM_PROBE_FAIL -4
|
||||
|
||||
// Define Grbl feedback message codes.
|
||||
#define MESSAGE_CRITICAL_EVENT 1
|
||||
#define MESSAGE_ALARM_LOCK 2
|
||||
#define MESSAGE_ALARM_UNLOCK 3
|
||||
#define MESSAGE_ENABLED 4
|
||||
#define MESSAGE_DISABLED 5
|
||||
|
||||
// Prints system status messages.
|
||||
void report_status_message(uint8_t status_code);
|
||||
|
||||
// Prints system alarm messages.
|
||||
void report_alarm_message(int8_t alarm_code);
|
||||
|
||||
// Prints miscellaneous feedback messages.
|
||||
void report_feedback_message(uint8_t message_code);
|
||||
|
||||
// Prints welcome message
|
||||
void report_init_message();
|
||||
|
||||
// Prints Grbl help and current global settings
|
||||
void report_grbl_help();
|
||||
|
||||
// Prints Grbl global settings
|
||||
void report_grbl_settings();
|
||||
|
||||
// Prints realtime status report
|
||||
void report_realtime_status();
|
||||
|
||||
// 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();
|
||||
|
||||
// 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
|
205
grbl/serial.c
Normal file
205
grbl/serial.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
serial.c - Low level functions for sending and recieving bytes via the serial port
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
uint8_t serial_rx_buffer[RX_BUFFER_SIZE];
|
||||
uint8_t serial_rx_buffer_head = 0;
|
||||
volatile uint8_t serial_rx_buffer_tail = 0;
|
||||
|
||||
uint8_t serial_tx_buffer[TX_BUFFER_SIZE];
|
||||
uint8_t serial_tx_buffer_head = 0;
|
||||
volatile uint8_t serial_tx_buffer_tail = 0;
|
||||
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
|
||||
#endif
|
||||
|
||||
|
||||
// Returns the number of bytes used in the RX serial buffer.
|
||||
uint8_t serial_get_rx_buffer_count()
|
||||
{
|
||||
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
|
||||
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
|
||||
return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head));
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of bytes used in the TX serial buffer.
|
||||
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
|
||||
uint8_t serial_get_tx_buffer_count()
|
||||
{
|
||||
uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
|
||||
if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); }
|
||||
return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head));
|
||||
}
|
||||
|
||||
|
||||
void serial_init()
|
||||
{
|
||||
// Set baud rate
|
||||
#if BAUD_RATE < 57600
|
||||
uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ;
|
||||
UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX
|
||||
#else
|
||||
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
|
||||
UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200
|
||||
#endif
|
||||
UBRR0H = UBRR0_value >> 8;
|
||||
UBRR0L = UBRR0_value;
|
||||
|
||||
// enable rx and tx
|
||||
UCSR0B |= 1<<RXEN0;
|
||||
UCSR0B |= 1<<TXEN0;
|
||||
|
||||
// enable interrupt on complete reception of a byte
|
||||
UCSR0B |= 1<<RXCIE0;
|
||||
|
||||
// defaults to 8-bit, no parity, 1 stop bit
|
||||
}
|
||||
|
||||
|
||||
// Writes one byte to the TX serial buffer. Called by main program.
|
||||
// TODO: Check if we can speed this up for writing strings, rather than single bytes.
|
||||
void serial_write(uint8_t data) {
|
||||
// Calculate next head
|
||||
uint8_t next_head = serial_tx_buffer_head + 1;
|
||||
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
|
||||
|
||||
// Wait until there is space in the buffer
|
||||
while (next_head == serial_tx_buffer_tail) {
|
||||
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
|
||||
if (sys.rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
|
||||
}
|
||||
|
||||
// Store data and advance head
|
||||
serial_tx_buffer[serial_tx_buffer_head] = data;
|
||||
serial_tx_buffer_head = next_head;
|
||||
|
||||
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
|
||||
UCSR0B |= (1 << UDRIE0);
|
||||
}
|
||||
|
||||
|
||||
// Data Register Empty Interrupt handler
|
||||
ISR(SERIAL_UDRE)
|
||||
{
|
||||
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if (flow_ctrl == SEND_XOFF) {
|
||||
UDR0 = XOFF_CHAR;
|
||||
flow_ctrl = XOFF_SENT;
|
||||
} else if (flow_ctrl == SEND_XON) {
|
||||
UDR0 = XON_CHAR;
|
||||
flow_ctrl = XON_SENT;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Send a byte from the buffer
|
||||
UDR0 = serial_tx_buffer[tail];
|
||||
|
||||
// Update tail position
|
||||
tail++;
|
||||
if (tail == TX_BUFFER_SIZE) { tail = 0; }
|
||||
|
||||
serial_tx_buffer_tail = tail;
|
||||
}
|
||||
|
||||
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
|
||||
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
|
||||
}
|
||||
|
||||
|
||||
// Fetches the first byte in the serial read buffer. Called by main program.
|
||||
uint8_t serial_read()
|
||||
{
|
||||
uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
|
||||
if (serial_rx_buffer_head == tail) {
|
||||
return SERIAL_NO_DATA;
|
||||
} else {
|
||||
uint8_t data = serial_rx_buffer[tail];
|
||||
|
||||
tail++;
|
||||
if (tail == RX_BUFFER_SIZE) { tail = 0; }
|
||||
serial_rx_buffer_tail = tail;
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
|
||||
flow_ctrl = SEND_XON;
|
||||
UCSR0B |= (1 << UDRIE0); // Force TX
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ISR(SERIAL_RX)
|
||||
{
|
||||
uint8_t data = UDR0;
|
||||
uint8_t next_head;
|
||||
|
||||
// Pick off realtime command characters directly from the serial stream. These characters are
|
||||
// not passed into the buffer, but these set system state flag bits for realtime execution.
|
||||
switch (data) {
|
||||
case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true
|
||||
case CMD_CYCLE_START: bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true
|
||||
case CMD_FEED_HOLD: bit_true_atomic(sys.rt_exec_state, 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 = serial_rx_buffer_head + 1;
|
||||
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
|
||||
|
||||
// Write data to buffer unless it is full.
|
||||
if (next_head != serial_rx_buffer_tail) {
|
||||
serial_rx_buffer[serial_rx_buffer_head] = data;
|
||||
serial_rx_buffer_head = next_head;
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
|
||||
flow_ctrl = SEND_XOFF;
|
||||
UCSR0B |= (1 << UDRIE0); // Force TX
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
//TODO: else alarm on overflow?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serial_reset_read_buffer()
|
||||
{
|
||||
serial_rx_buffer_tail = serial_rx_buffer_head;
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
flow_ctrl = XON_SENT;
|
||||
#endif
|
||||
}
|
69
grbl/serial.h
Normal file
69
grbl/serial.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
serial.c - Low level functions for sending and recieving bytes via the serial port
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef serial_h
|
||||
#define serial_h
|
||||
|
||||
|
||||
#ifndef RX_BUFFER_SIZE
|
||||
#define RX_BUFFER_SIZE 128
|
||||
#endif
|
||||
#ifndef TX_BUFFER_SIZE
|
||||
#define TX_BUFFER_SIZE 64
|
||||
#endif
|
||||
|
||||
#define SERIAL_NO_DATA 0xff
|
||||
|
||||
#ifdef ENABLE_XONXOFF
|
||||
#define RX_BUFFER_FULL 96 // XOFF high watermark
|
||||
#define RX_BUFFER_LOW 64 // XON low watermark
|
||||
#define SEND_XOFF 1
|
||||
#define SEND_XON 2
|
||||
#define XOFF_SENT 3
|
||||
#define XON_SENT 4
|
||||
#define XOFF_CHAR 0x13
|
||||
#define XON_CHAR 0x11
|
||||
#endif
|
||||
|
||||
void serial_init();
|
||||
|
||||
// Writes one byte to the TX serial buffer. Called by main program.
|
||||
void serial_write(uint8_t data);
|
||||
|
||||
// Fetches the first byte in the serial read buffer. Called by main program.
|
||||
uint8_t serial_read();
|
||||
|
||||
// Reset and empty data in read buffer. Used by e-stop and reset.
|
||||
void serial_reset_read_buffer();
|
||||
|
||||
// Returns the number of bytes used in the RX serial buffer.
|
||||
uint8_t serial_get_rx_buffer_count();
|
||||
|
||||
// Returns the number of bytes used in the TX serial buffer.
|
||||
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
|
||||
uint8_t serial_get_tx_buffer_count();
|
||||
|
||||
#endif
|
339
grbl/settings.c
Normal file
339
grbl/settings.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
settings.c - eeprom configuration handling
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
settings_t settings;
|
||||
|
||||
|
||||
// Method to store startup lines into EEPROM
|
||||
void settings_store_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
|
||||
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
|
||||
// Method to store build info into EEPROM
|
||||
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)
|
||||
{
|
||||
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
|
||||
memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS);
|
||||
}
|
||||
|
||||
|
||||
// Method to store Grbl global settings struct and version number into EEPROM
|
||||
void write_global_settings()
|
||||
{
|
||||
eeprom_put_char(0, SETTINGS_VERSION);
|
||||
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
|
||||
}
|
||||
|
||||
|
||||
// Method to restore EEPROM-saved Grbl global settings back to defaults.
|
||||
void settings_restore_global_settings() {
|
||||
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
|
||||
settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME;
|
||||
settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
|
||||
settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
|
||||
settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK;
|
||||
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
|
||||
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
|
||||
settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
|
||||
settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
|
||||
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
|
||||
settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
|
||||
settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
|
||||
|
||||
settings.flags = 0;
|
||||
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
|
||||
if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
|
||||
if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
|
||||
if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
|
||||
if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; }
|
||||
if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; }
|
||||
|
||||
settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM;
|
||||
settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM;
|
||||
settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM;
|
||||
settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
|
||||
settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
|
||||
settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
|
||||
settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
|
||||
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
|
||||
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
|
||||
settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL);
|
||||
settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL);
|
||||
settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL);
|
||||
|
||||
write_global_settings();
|
||||
}
|
||||
|
||||
|
||||
// Helper function to clear the EEPROM space containing parameter data.
|
||||
void settings_clear_parameters() {
|
||||
uint8_t idx;
|
||||
float coord_data[N_AXIS];
|
||||
memset(&coord_data, 0, sizeof(coord_data));
|
||||
for (idx=0; idx < SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); }
|
||||
}
|
||||
|
||||
|
||||
// Helper function to clear the EEPROM space containing the startup lines.
|
||||
void settings_clear_startup_lines() {
|
||||
#if N_STARTUP_LINE > 0
|
||||
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0);
|
||||
#endif
|
||||
#if N_STARTUP_LINE > 1
|
||||
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Helper function to clear the EEPROM space containing the user build info string.
|
||||
void settings_clear_build_info() { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); }
|
||||
|
||||
|
||||
// Reads startup line from EEPROM. Updated pointed line string data.
|
||||
uint8_t settings_read_startup_line(uint8_t n, char *line)
|
||||
{
|
||||
uint32_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; // Empty line
|
||||
settings_store_startup_line(n, line);
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
|
||||
if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) {
|
||||
// Reset with default zero vector
|
||||
clear_vector_float(coord_data);
|
||||
settings_write_coord_data(coord_select,coord_data);
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
// A helper method to set settings from command line
|
||||
uint8_t settings_store_global_setting(uint8_t parameter, float value) {
|
||||
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
|
||||
if (parameter >= AXIS_SETTINGS_START_VAL) {
|
||||
// Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
|
||||
// NOTE: Ensure the setting index corresponds to the report.c settings printout.
|
||||
parameter -= AXIS_SETTINGS_START_VAL;
|
||||
uint8_t set_idx = 0;
|
||||
while (set_idx < AXIS_N_SETTINGS) {
|
||||
if (parameter < N_AXIS) {
|
||||
// Valid axis setting found.
|
||||
switch (set_idx) {
|
||||
case 0:
|
||||
// if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
|
||||
settings.steps_per_mm[parameter] = value;
|
||||
break;
|
||||
case 1:
|
||||
// if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
|
||||
settings.max_rate[parameter] = value;
|
||||
break;
|
||||
case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
|
||||
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
|
||||
}
|
||||
break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
|
||||
} else {
|
||||
set_idx++;
|
||||
// If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
|
||||
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); }
|
||||
parameter -= AXIS_SETTINGS_INCREMENT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Store non-axis Grbl settings
|
||||
uint8_t int_value = trunc(value);
|
||||
switch(parameter) {
|
||||
case 0:
|
||||
if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
|
||||
settings.pulse_microseconds = int_value; break;
|
||||
case 1: settings.stepper_idle_lock_time = int_value; break;
|
||||
case 2:
|
||||
settings.step_invert_mask = int_value;
|
||||
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
|
||||
break;
|
||||
case 3:
|
||||
settings.dir_invert_mask = int_value;
|
||||
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
|
||||
break;
|
||||
case 4: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
|
||||
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; }
|
||||
break;
|
||||
case 5: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
|
||||
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
|
||||
break;
|
||||
case 6: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; }
|
||||
else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; }
|
||||
break;
|
||||
case 10: settings.status_report_mask = int_value; break;
|
||||
case 11: settings.junction_deviation = value; break;
|
||||
case 12: settings.arc_tolerance = value; break;
|
||||
case 13:
|
||||
if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; }
|
||||
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
|
||||
break;
|
||||
case 14: // Reset to ensure change. Immediate re-init may cause problems.
|
||||
if (int_value) { settings.flags |= BITFLAG_AUTO_START; }
|
||||
else { settings.flags &= ~BITFLAG_AUTO_START; }
|
||||
break;
|
||||
case 20:
|
||||
if (int_value) {
|
||||
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
|
||||
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
|
||||
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
|
||||
break;
|
||||
case 21:
|
||||
if (int_value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; }
|
||||
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
|
||||
break;
|
||||
case 22:
|
||||
if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
|
||||
else {
|
||||
settings.flags &= ~BITFLAG_HOMING_ENABLE;
|
||||
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
|
||||
}
|
||||
break;
|
||||
case 23: settings.homing_dir_mask = int_value; break;
|
||||
case 24: settings.homing_feed_rate = value; break;
|
||||
case 25: settings.homing_seek_rate = value; break;
|
||||
case 26: settings.homing_debounce_delay = int_value; break;
|
||||
case 27: settings.homing_pulloff = value; break;
|
||||
default:
|
||||
return(STATUS_INVALID_STATEMENT);
|
||||
}
|
||||
}
|
||||
write_global_settings();
|
||||
return(STATUS_OK);
|
||||
}
|
||||
|
||||
|
||||
// Initialize the config subsystem
|
||||
void settings_init() {
|
||||
if(!read_global_settings()) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
|
||||
settings_restore_global_settings();
|
||||
|
||||
// Force clear startup lines and build info user data. Parameters should be ok.
|
||||
// TODO: For next version, remove these clears. Only here because line buffer increased.
|
||||
settings_clear_startup_lines();
|
||||
settings_clear_build_info();
|
||||
|
||||
report_grbl_settings();
|
||||
}
|
||||
|
||||
// Check all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing.
|
||||
float coord_data[N_AXIS];
|
||||
uint8_t i;
|
||||
for (i=0; i<=SETTING_INDEX_NCOORD; i++) {
|
||||
if (!settings_read_coord_data(i, coord_data)) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
}
|
||||
}
|
||||
// NOTE: Startup lines are checked and executed by protocol_main_loop at the end of initialization.
|
||||
// TODO: Build info should be checked here, but will wait until v1.0 to address this. Ok for now.
|
||||
}
|
||||
|
||||
|
||||
// Returns step pin mask according to Grbl internal axis indexing.
|
||||
uint8_t get_step_pin_mask(uint8_t axis_idx)
|
||||
{
|
||||
if ( axis_idx == X_AXIS ) { return((1<<X_STEP_BIT)); }
|
||||
if ( axis_idx == Y_AXIS ) { return((1<<Y_STEP_BIT)); }
|
||||
return((1<<Z_STEP_BIT));
|
||||
}
|
||||
|
||||
|
||||
// Returns direction pin mask according to Grbl internal axis indexing.
|
||||
uint8_t get_direction_pin_mask(uint8_t axis_idx)
|
||||
{
|
||||
if ( axis_idx == X_AXIS ) { return((1<<X_DIRECTION_BIT)); }
|
||||
if ( axis_idx == Y_AXIS ) { return((1<<Y_DIRECTION_BIT)); }
|
||||
return((1<<Z_DIRECTION_BIT));
|
||||
}
|
||||
|
||||
|
||||
// Returns limit pin mask according to Grbl internal axis indexing.
|
||||
uint8_t get_limit_pin_mask(uint8_t axis_idx)
|
||||
{
|
||||
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); }
|
||||
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); }
|
||||
return((1<<Z_LIMIT_BIT));
|
||||
}
|
142
grbl/settings.h
Normal file
142
grbl/settings.h
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
settings.h - eeprom configuration handling
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef settings_h
|
||||
#define settings_h
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
|
||||
// when firmware is upgraded. Always stored in byte 0 of eeprom
|
||||
#define SETTINGS_VERSION 9 // NOTE: Check settings_reset() when moving to next version.
|
||||
|
||||
// Define bit flag masks for the boolean settings in settings.flag.
|
||||
#define BITFLAG_REPORT_INCHES bit(0)
|
||||
#define BITFLAG_AUTO_START bit(1)
|
||||
#define BITFLAG_INVERT_ST_ENABLE bit(2)
|
||||
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
|
||||
#define BITFLAG_HOMING_ENABLE bit(4)
|
||||
#define BITFLAG_SOFT_LIMIT_ENABLE bit(5)
|
||||
#define BITFLAG_INVERT_LIMIT_PINS bit(6)
|
||||
#define BITFLAG_INVERT_PROBE_PIN bit(7)
|
||||
|
||||
// Define status reporting boolean enable bit flags in settings.status_report_mask
|
||||
#define BITFLAG_RT_STATUS_MACHINE_POSITION bit(0)
|
||||
#define BITFLAG_RT_STATUS_WORK_POSITION bit(1)
|
||||
#define BITFLAG_RT_STATUS_PLANNER_BUFFER bit(2)
|
||||
#define BITFLAG_RT_STATUS_SERIAL_RX bit(3)
|
||||
|
||||
// Define EEPROM memory address location values for Grbl settings and parameters
|
||||
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
|
||||
// the startup script. The lower half contains the global settings and space for future
|
||||
// developments.
|
||||
#define EEPROM_ADDR_GLOBAL 1U
|
||||
#define EEPROM_ADDR_PARAMETERS 512U
|
||||
#define EEPROM_ADDR_STARTUP_BLOCK 768U
|
||||
#define EEPROM_ADDR_BUILD_INFO 942U
|
||||
|
||||
// Define EEPROM address indexing for coordinate parameters
|
||||
#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1)
|
||||
#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0)
|
||||
// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59)
|
||||
#define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1
|
||||
#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2
|
||||
// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported)
|
||||
|
||||
// Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS.
|
||||
#define AXIS_N_SETTINGS 4
|
||||
#define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255.
|
||||
#define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings
|
||||
|
||||
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
|
||||
typedef struct {
|
||||
// Axis settings
|
||||
float steps_per_mm[N_AXIS];
|
||||
float max_rate[N_AXIS];
|
||||
float acceleration[N_AXIS];
|
||||
float max_travel[N_AXIS];
|
||||
|
||||
// Remaining Grbl settings
|
||||
uint8_t pulse_microseconds;
|
||||
uint8_t step_invert_mask;
|
||||
uint8_t dir_invert_mask;
|
||||
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
|
||||
uint8_t status_report_mask; // Mask to indicate desired report data.
|
||||
float junction_deviation;
|
||||
float arc_tolerance;
|
||||
|
||||
uint8_t flags; // Contains default boolean settings
|
||||
|
||||
uint8_t homing_dir_mask;
|
||||
float homing_feed_rate;
|
||||
float homing_seek_rate;
|
||||
uint16_t homing_debounce_delay;
|
||||
float homing_pulloff;
|
||||
} settings_t;
|
||||
extern settings_t settings;
|
||||
|
||||
// Initialize the configuration subsystem (load settings from EEPROM)
|
||||
void settings_init();
|
||||
|
||||
// Helper functions to clear and restore EEPROM defaults
|
||||
void settings_restore_global_settings();
|
||||
void settings_clear_parameters();
|
||||
void settings_clear_startup_line();
|
||||
void settings_clear_build_info();
|
||||
|
||||
// A helper method to set new settings from command line
|
||||
uint8_t settings_store_global_setting(uint8_t parameter, float value);
|
||||
|
||||
// Stores the protocol line variable as a startup line in EEPROM
|
||||
void settings_store_startup_line(uint8_t n, char *line);
|
||||
|
||||
// Reads an EEPROM startup line to the protocol line variable
|
||||
uint8_t settings_read_startup_line(uint8_t n, char *line);
|
||||
|
||||
// Stores build info user-defined string
|
||||
void settings_store_build_info(char *line);
|
||||
|
||||
// Reads build info user-defined string
|
||||
uint8_t settings_read_build_info(char *line);
|
||||
|
||||
// Writes selected coordinate data to EEPROM
|
||||
void settings_write_coord_data(uint8_t coord_select, float *coord_data);
|
||||
|
||||
// Reads selected coordinate data from EEPROM
|
||||
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data);
|
||||
|
||||
// Returns the step pin mask according to Grbl's internal axis numbering
|
||||
uint8_t get_step_pin_mask(uint8_t i);
|
||||
|
||||
// Returns the direction pin mask according to Grbl's internal axis numbering
|
||||
uint8_t get_direction_pin_mask(uint8_t i);
|
||||
|
||||
// Returns the limit pin mask according to Grbl's internal axis numbering
|
||||
uint8_t get_limit_pin_mask(uint8_t i);
|
||||
|
||||
|
||||
#endif
|
116
grbl/spindle_control.c
Normal file
116
grbl/spindle_control.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
spindle_control.c - spindle control methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
void spindle_init()
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
// 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_set_state(uint8_t state, float rpm)
|
||||
{
|
||||
// Halt or set spindle direction and rpm.
|
||||
if (state == SPINDLE_DISABLE) {
|
||||
|
||||
spindle_stop();
|
||||
|
||||
} else {
|
||||
|
||||
if (state == SPINDLE_ENABLE_CW) {
|
||||
SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT);
|
||||
} else {
|
||||
SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT);
|
||||
}
|
||||
|
||||
#ifdef VARIABLE_SPINDLE
|
||||
// TODO: Install the optional capability for frequency-based output for servos.
|
||||
#ifdef CPU_MAP_ATMEGA2560
|
||||
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
|
||||
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02 | (1<<WAVE2_REGISTER) | (1<<WAVE3_REGISTER); // set to 1/8 Prescaler
|
||||
OCR4A = 0xFFFF; // set the top 16bit value
|
||||
uint16_t current_pwm;
|
||||
#else
|
||||
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
|
||||
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler
|
||||
uint8_t current_pwm;
|
||||
#endif
|
||||
|
||||
#define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
|
||||
if ( rpm < SPINDLE_MIN_RPM ) { rpm = 0; }
|
||||
else {
|
||||
rpm -= SPINDLE_MIN_RPM;
|
||||
if ( rpm > SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent integer overflow
|
||||
}
|
||||
current_pwm = floor( rpm*(PWM_MAX_VALUE/SPINDLE_RPM_RANGE) + 0.5);
|
||||
#ifdef MINIMUM_SPINDLE_PWM
|
||||
if (current_pwm < MINIMUM_SPINDLE_PWM) { current_pwm = MINIMUM_SPINDLE_PWM; }
|
||||
#endif
|
||||
OCR_REGISTER = current_pwm; // Set PWM pin output
|
||||
|
||||
#ifdef CPU_MAP_ATMEGA2560 // 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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void spindle_run(uint8_t state, float rpm)
|
||||
{
|
||||
if (sys.state == STATE_CHECK_MODE) { return; }
|
||||
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
|
||||
spindle_set_state(state, rpm);
|
||||
}
|
42
grbl/spindle_control.h
Normal file
42
grbl/spindle_control.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
spindle_control.h - spindle control methods
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef spindle_control_h
|
||||
#define spindle_control_h
|
||||
|
||||
|
||||
// Initializes spindle pins and hardware PWM, if enabled.
|
||||
void spindle_init();
|
||||
|
||||
// Sets spindle direction and spindle rpm via PWM, if enabled.
|
||||
void spindle_run(uint8_t direction, float rpm);
|
||||
|
||||
void spindle_set_state(uint8_t state, float rpm);
|
||||
|
||||
// Kills spindle.
|
||||
void spindle_stop();
|
||||
|
||||
#endif
|
856
grbl/stepper.c
Normal file
856
grbl/stepper.c
Normal file
@ -0,0 +1,856 @@
|
||||
/*
|
||||
stepper.c - stepper motor driver: executes motion plans using stepper motors
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011-2012 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
|
||||
// Some useful constants.
|
||||
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
|
||||
#define REQ_MM_INCREMENT_SCALAR 1.25
|
||||
#define RAMP_ACCEL 0
|
||||
#define RAMP_CRUISE 1
|
||||
#define RAMP_DECEL 2
|
||||
|
||||
// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level
|
||||
// frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin
|
||||
// starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
|
||||
// be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit
|
||||
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
|
||||
// Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
|
||||
// NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency.
|
||||
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
|
||||
// and timer accuracy. Do not alter these settings unless you know what you are doing.
|
||||
#define MAX_AMASS_LEVEL 3
|
||||
// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
|
||||
#define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
|
||||
#define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4)
|
||||
#define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)
|
||||
|
||||
|
||||
// Stores the planner block Bresenham algorithm execution data for the segments in the segment
|
||||
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
|
||||
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
|
||||
// NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be
|
||||
// discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this
|
||||
// data for its own use.
|
||||
typedef struct {
|
||||
uint8_t direction_bits;
|
||||
uint32_t steps[N_AXIS];
|
||||
uint32_t step_event_count;
|
||||
} st_block_t;
|
||||
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
|
||||
|
||||
// Primary stepper segment ring buffer. Contains small, short line segments for the stepper
|
||||
// algorithm to execute, which are "checked-out" incrementally from the first block in the
|
||||
// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by
|
||||
// the planner, where the remaining planner block steps still can.
|
||||
typedef struct {
|
||||
uint16_t n_step; // Number of step events to be executed for this segment
|
||||
uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment.
|
||||
uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate.
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment
|
||||
#else
|
||||
uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing.
|
||||
#endif
|
||||
} segment_t;
|
||||
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
|
||||
|
||||
// Stepper ISR data struct. Contains the running data for the main stepper ISR.
|
||||
typedef struct {
|
||||
// Used by the bresenham line algorithm
|
||||
uint32_t counter_x, // Counter variables for the bresenham line tracer
|
||||
counter_y,
|
||||
counter_z;
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
|
||||
#endif
|
||||
|
||||
uint8_t execute_step; // Flags step execution for each interrupt.
|
||||
uint8_t step_pulse_time; // Step pulse reset time after step rise
|
||||
uint8_t step_outbits; // The next stepping-bits to be output
|
||||
uint8_t dir_outbits;
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
uint32_t steps[N_AXIS];
|
||||
#endif
|
||||
|
||||
uint16_t step_count; // Steps remaining in line segment motion
|
||||
uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block.
|
||||
st_block_t *exec_block; // Pointer to the block data for the segment being executed
|
||||
segment_t *exec_segment; // Pointer to the segment being executed
|
||||
} stepper_t;
|
||||
static stepper_t st;
|
||||
|
||||
// Step segment ring buffer indices
|
||||
static volatile uint8_t segment_buffer_tail;
|
||||
static uint8_t segment_buffer_head;
|
||||
static uint8_t segment_next_head;
|
||||
|
||||
// Step and direction port invert masks.
|
||||
static uint8_t step_port_invert_mask;
|
||||
static uint8_t dir_port_invert_mask;
|
||||
|
||||
// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though.
|
||||
static volatile uint8_t busy;
|
||||
|
||||
// Pointers for the step segment being prepped from the planner buffer. Accessed only by the
|
||||
// main program. Pointers may be planning segments or planner blocks ahead of what being executed.
|
||||
static plan_block_t *pl_block; // Pointer to the planner block being prepped
|
||||
static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped
|
||||
|
||||
// Segment preparation data struct. Contains all the necessary information to compute new segments
|
||||
// based on the current executing planner block.
|
||||
typedef struct {
|
||||
uint8_t st_block_index; // Index of stepper common data block being prepped
|
||||
uint8_t flag_partial_block; // Flag indicating the last block completed. Time to load a new one.
|
||||
|
||||
float steps_remaining;
|
||||
float step_per_mm; // Current planner block step/millimeter conversion scalar
|
||||
float req_mm_increment;
|
||||
float dt_remainder;
|
||||
|
||||
uint8_t ramp_type; // Current segment ramp state
|
||||
float mm_complete; // End of velocity profile from end of current planner block in (mm).
|
||||
// NOTE: This value must coincide with a step(no mantissa) when converted.
|
||||
float current_speed; // Current speed at the end of the segment buffer (mm/min)
|
||||
float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min)
|
||||
float exit_speed; // Exit speed of executing block (mm/min)
|
||||
float accelerate_until; // Acceleration ramp end measured from end of block (mm)
|
||||
float decelerate_after; // Deceleration ramp start measured from end of block (mm)
|
||||
} st_prep_t;
|
||||
static st_prep_t prep;
|
||||
|
||||
|
||||
/* BLOCK VELOCITY PROFILE DEFINITION
|
||||
__________________________
|
||||
/| |\ _________________ ^
|
||||
/ | | \ /| |\ |
|
||||
/ | | \ / | | \ s
|
||||
/ | | | | | \ p
|
||||
/ | | | | | \ e
|
||||
+-----+------------------------+---+--+---------------+----+ e
|
||||
| BLOCK 1 ^ BLOCK 2 | d
|
||||
|
|
||||
time -----> EXAMPLE: Block 2 entry speed is at max junction velocity
|
||||
|
||||
The planner block buffer is planned assuming constant acceleration velocity profiles and are
|
||||
continuously joined at block junctions as shown above. However, the planner only actively computes
|
||||
the block entry speeds for an optimal velocity plan, but does not compute the block internal
|
||||
velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the
|
||||
stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise-
|
||||
deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and
|
||||
triangle(no cruise).
|
||||
|
||||
maximum_speed (< nominal_speed) -> +
|
||||
+--------+ <- maximum_speed (= nominal_speed) /|\
|
||||
/ \ / | \
|
||||
current_speed -> + \ / | + <- exit_speed
|
||||
| + <- exit_speed / | |
|
||||
+-------------+ current_speed -> +----+--+
|
||||
time --> ^ ^ ^ ^
|
||||
| | | |
|
||||
decelerate_after(in mm) decelerate_after(in mm)
|
||||
^ ^ ^ ^
|
||||
| | | |
|
||||
accelerate_until(in mm) accelerate_until(in mm)
|
||||
|
||||
The step segment buffer computes the executing block velocity profile and tracks the critical
|
||||
parameters for the stepper algorithm to accurately trace the profile. These critical parameters
|
||||
are shown and defined in the above illustration.
|
||||
*/
|
||||
|
||||
|
||||
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
|
||||
// enabled. Startup init and limits call this function but shouldn't start the cycle.
|
||||
void st_wake_up()
|
||||
{
|
||||
// Enable stepper drivers.
|
||||
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
|
||||
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
|
||||
|
||||
if (sys.state & (STATE_CYCLE | STATE_HOMING)){
|
||||
// Initialize stepper output bits
|
||||
st.dir_outbits = dir_port_invert_mask;
|
||||
st.step_outbits = step_port_invert_mask;
|
||||
|
||||
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
|
||||
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||
// Set delay between direction pin write and step command.
|
||||
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#else // Normal operation
|
||||
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
|
||||
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
|
||||
#endif
|
||||
|
||||
// Enable Stepper Driver Interrupt
|
||||
TIMSK1 |= (1<<OCIE1A);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Stepper shutdown
|
||||
void st_go_idle()
|
||||
{
|
||||
// Disable Stepper Driver Interrupt. Allow Stepper Port Reset Interrupt to finish, if active.
|
||||
TIMSK1 &= ~(1<<OCIE1A); // Disable Timer1 interrupt
|
||||
TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Reset clock to no prescaling.
|
||||
busy = false;
|
||||
|
||||
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
|
||||
bool pin_state = false; // Keep enabled.
|
||||
if (((settings.stepper_idle_lock_time != 0xff) || sys.rt_exec_alarm) && sys.state != STATE_HOMING) {
|
||||
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
|
||||
// stop and not drift from residual inertial forces at the end of the last movement.
|
||||
delay_ms(settings.stepper_idle_lock_time);
|
||||
pin_state = true; // Override. Disable steppers.
|
||||
}
|
||||
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { pin_state = !pin_state; } // Apply pin invert.
|
||||
if (pin_state) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
|
||||
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
|
||||
}
|
||||
|
||||
|
||||
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. Grbl employs
|
||||
the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves.
|
||||
Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical
|
||||
round-off errors and only requires fast integer counters, meaning low computational overhead
|
||||
and maximizing the Arduino's capabilities. However, the downside of the Bresenham algorithm
|
||||
is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step
|
||||
pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is
|
||||
particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but
|
||||
is usually not a physical problem at higher frequencies, although audible.
|
||||
To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis
|
||||
Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies,
|
||||
AMASS artificially increases the Bresenham resolution without effecting the algorithm's
|
||||
innate exactness. AMASS adapts its resolution levels automatically depending on the step
|
||||
frequency to be executed, meaning that for even lower step frequencies the step smoothing
|
||||
level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham
|
||||
step count for each AMASS level. For example, for a Level 1 step smoothing, we bit shift
|
||||
the Bresenham step event count, effectively multiplying it by 2, while the axis step counts
|
||||
remain the same, and then double the stepper ISR frequency. In effect, we are allowing the
|
||||
non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is
|
||||
stepping every two ISR ticks, rather than every ISR tick in the traditional sense. At AMASS
|
||||
Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any
|
||||
of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the
|
||||
stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing
|
||||
issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but
|
||||
in fact, more efficiently utilizes unused CPU cycles overall throughout all configurations.
|
||||
AMASS retains the Bresenham algorithm exactness by requiring that it always executes a full
|
||||
Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four
|
||||
intermediate steps must be completed such that baseline Bresenham (Level 0) count is always
|
||||
retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed.
|
||||
Although the AMASS Levels are in reality arbitrary, where the baseline Bresenham counts can
|
||||
be multiplied by any integer value, multiplication by powers of two are simply used to ease
|
||||
CPU overhead with bitshift integer operations.
|
||||
This interrupt is simple and dumb by design. All the computational heavy-lifting, as in
|
||||
determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments,
|
||||
defined as constant velocity over n number of steps, from the step segment buffer and then
|
||||
executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This
|
||||
ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port
|
||||
after each pulse. The bresenham line tracer algorithm controls all stepper outputs
|
||||
simultaneously with these two interrupts.
|
||||
|
||||
NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick,
|
||||
which for Grbl must be less than 33.3usec (@30kHz ISR rate). Oscilloscope measured time in
|
||||
ISR is 5usec typical and 25usec maximum, well below requirement.
|
||||
NOTE: This ISR expects at least one step to be executed per segment.
|
||||
*/
|
||||
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
|
||||
// int8 variables and update position counters only when a segment completes. This can get complicated
|
||||
// with probing and homing cycles that require true real-time positions.
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
||||
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
|
||||
|
||||
// Set the direction pins a couple of nanoseconds before we step the steppers
|
||||
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
|
||||
|
||||
// Then pulse the stepping pins
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
st.step_bits = (STEP_PORT & ~STEP_MASK) | st.step_outbits; // Store out_bits to prevent overwriting.
|
||||
#else // Normal operation
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
|
||||
#endif
|
||||
|
||||
// Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
|
||||
// exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
|
||||
TCNT0 = st.step_pulse_time; // Reload Timer0 counter
|
||||
TCCR0B = (1<<CS01); // Begin Timer0. Full speed, 1/8 prescaler
|
||||
|
||||
busy = true;
|
||||
sei(); // Re-enable interrupts to allow Stepper Port Reset Interrupt to fire on-time.
|
||||
// NOTE: The remaining code in this ISR will finish before returning to main program.
|
||||
|
||||
// If there is no step segment, attempt to pop one from the stepper buffer
|
||||
if (st.exec_segment == NULL) {
|
||||
// Anything in the buffer? If so, load and initialize next step segment.
|
||||
if (segment_buffer_head != segment_buffer_tail) {
|
||||
// Initialize new step segment and load number of steps to execute
|
||||
st.exec_segment = &segment_buffer[segment_buffer_tail];
|
||||
|
||||
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
// With AMASS is disabled, set timer prescaler for segments with slow step frequencies (< 250Hz).
|
||||
TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
|
||||
#endif
|
||||
|
||||
// Initialize step segment timing per step and load number of steps to execute.
|
||||
OCR1A = st.exec_segment->cycles_per_tick;
|
||||
st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow.
|
||||
// If the new segment starts a new planner block, initialize stepper variables and counters.
|
||||
// NOTE: When the segment data index changes, this indicates a new planner block.
|
||||
if ( st.exec_block_index != st.exec_segment->st_block_index ) {
|
||||
st.exec_block_index = st.exec_segment->st_block_index;
|
||||
st.exec_block = &st_block_buffer[st.exec_block_index];
|
||||
|
||||
// Initialize Bresenham line and distance counters
|
||||
st.counter_x = (st.exec_block->step_event_count >> 1);
|
||||
st.counter_y = st.counter_x;
|
||||
st.counter_z = st.counter_x;
|
||||
}
|
||||
|
||||
st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask;
|
||||
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
// With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
|
||||
st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
|
||||
st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level;
|
||||
st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
// Segment buffer empty. Shutdown.
|
||||
st_go_idle();
|
||||
bit_true_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP); // Flag main program for cycle end
|
||||
return; // Nothing to do but exit.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check probing state.
|
||||
probe_state_monitor();
|
||||
|
||||
// Reset step out bits.
|
||||
st.step_outbits = 0;
|
||||
|
||||
// Execute step displacement profile by Bresenham line algorithm
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
st.counter_x += st.steps[X_AXIS];
|
||||
#else
|
||||
st.counter_x += st.exec_block->steps[X_AXIS];
|
||||
#endif
|
||||
if (st.counter_x > st.exec_block->step_event_count) {
|
||||
st.step_outbits |= (1<<X_STEP_BIT);
|
||||
st.counter_x -= st.exec_block->step_event_count;
|
||||
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
|
||||
else { sys.position[X_AXIS]++; }
|
||||
}
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
st.counter_y += st.steps[Y_AXIS];
|
||||
#else
|
||||
st.counter_y += st.exec_block->steps[Y_AXIS];
|
||||
#endif
|
||||
if (st.counter_y > st.exec_block->step_event_count) {
|
||||
st.step_outbits |= (1<<Y_STEP_BIT);
|
||||
st.counter_y -= st.exec_block->step_event_count;
|
||||
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
|
||||
else { sys.position[Y_AXIS]++; }
|
||||
}
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
st.counter_z += st.steps[Z_AXIS];
|
||||
#else
|
||||
st.counter_z += st.exec_block->steps[Z_AXIS];
|
||||
#endif
|
||||
if (st.counter_z > st.exec_block->step_event_count) {
|
||||
st.step_outbits |= (1<<Z_STEP_BIT);
|
||||
st.counter_z -= st.exec_block->step_event_count;
|
||||
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
|
||||
else { sys.position[Z_AXIS]++; }
|
||||
}
|
||||
|
||||
// During a homing cycle, lock out and prevent desired axes from moving.
|
||||
if (sys.state == STATE_HOMING) { st.step_outbits &= sys.homing_axis_lock; }
|
||||
|
||||
st.step_count--; // Decrement step events count
|
||||
if (st.step_count == 0) {
|
||||
// Segment is complete. Discard current segment and advance segment indexing.
|
||||
st.exec_segment = NULL;
|
||||
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
|
||||
}
|
||||
|
||||
st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask
|
||||
busy = false;
|
||||
// SPINDLE_ENABLE_PORT ^= 1<<SPINDLE_ENABLE_BIT; // Debug: Used to time ISR
|
||||
}
|
||||
|
||||
|
||||
/* The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
|
||||
pulse. This should always trigger before the next Timer1 COMPA interrupt and independently
|
||||
finish, if Timer1 is disabled after completing a move.
|
||||
NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
|
||||
a few microseconds, if they execute right before one another. Not a big deal, but can
|
||||
cause issues at high step rates if another high frequency asynchronous interrupt is
|
||||
added to Grbl.
|
||||
*/
|
||||
// This interrupt is enabled by ISR_TIMER1_COMPAREA when it sets the motor port bits to execute
|
||||
// a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
|
||||
// completing one step cycle.
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
// Reset stepping pins (leave the direction pins)
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
|
||||
TCCR0B = 0; // Disable Timer0 to prevent re-entering this interrupt when it's not needed.
|
||||
}
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
// This interrupt is used only when STEP_PULSE_DELAY is enabled. Here, the step pulse is
|
||||
// initiated after the STEP_PULSE_DELAY time period has elapsed. The ISR TIMER2_OVF interrupt
|
||||
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
|
||||
// The new timing between direction, step pulse, and step complete events are setup in the
|
||||
// st_wake_up() routine.
|
||||
ISR(TIMER0_COMPA_vect)
|
||||
{
|
||||
STEP_PORT = st.step_bits; // Begin step pulse.
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Generates the step and direction port invert masks used in the Stepper Interrupt Driver.
|
||||
void st_generate_step_dir_invert_masks()
|
||||
{
|
||||
uint8_t idx;
|
||||
step_port_invert_mask = 0;
|
||||
dir_port_invert_mask = 0;
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
if (bit_istrue(settings.step_invert_mask,bit(idx))) { step_port_invert_mask |= get_step_pin_mask(idx); }
|
||||
if (bit_istrue(settings.dir_invert_mask,bit(idx))) { dir_port_invert_mask |= get_direction_pin_mask(idx); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset and clear stepper subsystem variables
|
||||
void st_reset()
|
||||
{
|
||||
// Initialize stepper driver idle state.
|
||||
st_go_idle();
|
||||
|
||||
// Initialize stepper algorithm variables.
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
memset(&st, 0, sizeof(st));
|
||||
st.exec_segment = NULL;
|
||||
pl_block = NULL; // Planner block pointer used by segment buffer
|
||||
segment_buffer_tail = 0;
|
||||
segment_buffer_head = 0; // empty = tail
|
||||
segment_next_head = 1;
|
||||
busy = false;
|
||||
|
||||
st_generate_step_dir_invert_masks();
|
||||
|
||||
// Initialize step and direction port pins.
|
||||
STEP_PORT = (STEP_PORT & ~STEP_MASK) | step_port_invert_mask;
|
||||
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | dir_port_invert_mask;
|
||||
}
|
||||
|
||||
|
||||
// Initialize and start the stepper motor subsystem
|
||||
void stepper_init()
|
||||
{
|
||||
// Configure step and direction interface pins
|
||||
STEP_DDR |= STEP_MASK;
|
||||
STEPPERS_DISABLE_DDR |= 1<<STEPPERS_DISABLE_BIT;
|
||||
DIRECTION_DDR |= DIRECTION_MASK;
|
||||
|
||||
// Configure Timer 1: Stepper Driver Interrupt
|
||||
TCCR1B &= ~(1<<WGM13); // waveform generation = 0100 = CTC
|
||||
TCCR1B |= (1<<WGM12);
|
||||
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
|
||||
TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0)); // Disconnect OC1 output
|
||||
// TCCR1B = (TCCR1B & ~((1<<CS12) | (1<<CS11))) | (1<<CS10); // Set in st_go_idle().
|
||||
// TIMSK1 &= ~(1<<OCIE1A); // Set in st_go_idle().
|
||||
|
||||
// Configure Timer 0: Stepper Port Reset Interrupt
|
||||
TIMSK0 &= ~((1<<OCIE0B) | (1<<OCIE0A) | (1<<TOIE0)); // Disconnect OC0 outputs and OVF interrupt.
|
||||
TCCR0A = 0; // Normal operation
|
||||
TCCR0B = 0; // Disable Timer0 until needed
|
||||
TIMSK0 |= (1<<TOIE0); // Enable Timer0 overflow interrupt
|
||||
#ifdef STEP_PULSE_DELAY
|
||||
TIMSK0 |= (1<<OCIE0A); // Enable Timer0 Compare Match A interrupt
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Called by planner_recalculate() when the executing block is updated by the new plan.
|
||||
void st_update_plan_block_parameters()
|
||||
{
|
||||
if (pl_block != NULL) { // Ignore if at start of a new block.
|
||||
prep.flag_partial_block = true;
|
||||
pl_block->entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed.
|
||||
pl_block = NULL; // Flag st_prep_segment() to load new velocity profile.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Prepares step segment buffer. Continuously called from main program.
|
||||
|
||||
The segment buffer is an intermediary buffer interface between the execution of steps
|
||||
by the stepper algorithm and the velocity profiles generated by the planner. The stepper
|
||||
algorithm only executes steps within the segment buffer and is filled by the main program
|
||||
when steps are "checked-out" from the first block in the planner buffer. This keeps the
|
||||
step execution and planning optimization processes atomic and protected from each other.
|
||||
The number of steps "checked-out" from the planner buffer and the number of segments in
|
||||
the segment buffer is sized and computed such that no operation in the main program takes
|
||||
longer than the time it takes the stepper algorithm to empty it before refilling it.
|
||||
Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
|
||||
NOTE: Computation units are in steps, millimeters, and minutes.
|
||||
*/
|
||||
void st_prep_buffer()
|
||||
{
|
||||
while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
|
||||
|
||||
// Determine if we need to load a new planner block or if the block has been replanned.
|
||||
if (pl_block == NULL) {
|
||||
pl_block = plan_get_current_block(); // Query planner for a queued block
|
||||
if (pl_block == NULL) { return; } // No planner blocks. Exit.
|
||||
|
||||
// Check if the segment buffer completed the last planner block. If so, load the Bresenham
|
||||
// data for the block. If not, we are still mid-block and the velocity profile was updated.
|
||||
if (prep.flag_partial_block) {
|
||||
prep.flag_partial_block = false; // Reset flag
|
||||
} else {
|
||||
// Increment stepper common data index to store new planner block data.
|
||||
if ( ++prep.st_block_index == (SEGMENT_BUFFER_SIZE-1) ) { prep.st_block_index = 0; }
|
||||
|
||||
// Prepare and copy Bresenham algorithm segment data from the new planner block, so that
|
||||
// when the segment buffer completes the planner block, it may be discarded when the
|
||||
// segment buffer finishes the prepped block, but the stepper ISR is still executing it.
|
||||
st_prep_block = &st_block_buffer[prep.st_block_index];
|
||||
st_prep_block->direction_bits = pl_block->direction_bits;
|
||||
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS];
|
||||
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS];
|
||||
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS];
|
||||
st_prep_block->step_event_count = pl_block->step_event_count;
|
||||
#else
|
||||
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
|
||||
// level, such that we never divide beyond the original data anywhere in the algorithm.
|
||||
// If the original data is divided, we can lose a step from integer roundoff.
|
||||
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS] << MAX_AMASS_LEVEL;
|
||||
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
|
||||
#endif
|
||||
|
||||
// Initialize segment buffer data for generating the segments.
|
||||
prep.steps_remaining = pl_block->step_event_count;
|
||||
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
|
||||
prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm;
|
||||
|
||||
prep.dt_remainder = 0.0; // Reset for new planner block
|
||||
|
||||
if (sys.state == STATE_HOLD) {
|
||||
// Override planner block entry speed and enforce deceleration during feed hold.
|
||||
prep.current_speed = prep.exit_speed;
|
||||
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
|
||||
}
|
||||
else { prep.current_speed = sqrt(pl_block->entry_speed_sqr); }
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------
|
||||
Compute the velocity profile of a new planner block based on its entry and exit
|
||||
speeds, or recompute the profile of a partially-completed planner block if the
|
||||
planner has updated it. For a commanded forced-deceleration, such as from a feed
|
||||
hold, override the planner velocities and decelerate to the target exit speed.
|
||||
*/
|
||||
prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block.
|
||||
float inv_2_accel = 0.5/pl_block->acceleration;
|
||||
if (sys.state == STATE_HOLD) { // [Forced Deceleration to Zero Velocity]
|
||||
// Compute velocity profile parameters for a feed hold in-progress. This profile overrides
|
||||
// the planner block profile, enforcing a deceleration to zero speed.
|
||||
prep.ramp_type = RAMP_DECEL;
|
||||
// Compute decelerate distance relative to end of block.
|
||||
float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr;
|
||||
if (decel_dist < 0.0) {
|
||||
// Deceleration through entire planner block. End of feed hold is not in this block.
|
||||
prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
|
||||
} else {
|
||||
prep.mm_complete = decel_dist; // End of feed hold.
|
||||
prep.exit_speed = 0.0;
|
||||
}
|
||||
} else { // [Normal Operation]
|
||||
// Compute or recompute velocity profile parameters of the prepped planner block.
|
||||
prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp.
|
||||
prep.accelerate_until = pl_block->millimeters;
|
||||
prep.exit_speed = plan_get_exec_block_exit_speed();
|
||||
float exit_speed_sqr = prep.exit_speed*prep.exit_speed;
|
||||
float intersect_distance =
|
||||
0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr));
|
||||
if (intersect_distance > 0.0) {
|
||||
if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types
|
||||
// NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0.
|
||||
prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr);
|
||||
if (prep.decelerate_after < intersect_distance) { // Trapezoid type
|
||||
prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr);
|
||||
if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) {
|
||||
// Cruise-deceleration or cruise-only type.
|
||||
prep.ramp_type = RAMP_CRUISE;
|
||||
} else {
|
||||
// Full-trapezoid or acceleration-cruise types
|
||||
prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr);
|
||||
}
|
||||
} else { // Triangle type
|
||||
prep.accelerate_until = intersect_distance;
|
||||
prep.decelerate_after = intersect_distance;
|
||||
prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr);
|
||||
}
|
||||
} else { // Deceleration-only type
|
||||
prep.ramp_type = RAMP_DECEL;
|
||||
// prep.decelerate_after = pl_block->millimeters;
|
||||
prep.maximum_speed = prep.current_speed;
|
||||
}
|
||||
} else { // Acceleration-only type
|
||||
prep.accelerate_until = 0.0;
|
||||
// prep.decelerate_after = 0.0;
|
||||
prep.maximum_speed = prep.exit_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize new segment
|
||||
segment_t *prep_segment = &segment_buffer[segment_buffer_head];
|
||||
|
||||
// Set new segment to point to the current segment data block.
|
||||
prep_segment->st_block_index = prep.st_block_index;
|
||||
|
||||
/*------------------------------------------------------------------------------------
|
||||
Compute the average velocity of this new segment by determining the total distance
|
||||
traveled over the segment time DT_SEGMENT. The following code first attempts to create
|
||||
a full segment based on the current ramp conditions. If the segment time is incomplete
|
||||
when terminating at a ramp state change, the code will continue to loop through the
|
||||
progressing ramp states to fill the remaining segment execution time. However, if
|
||||
an incomplete segment terminates at the end of the velocity profile, the segment is
|
||||
considered completed despite having a truncated execution time less than DT_SEGMENT.
|
||||
The velocity profile is always assumed to progress through the ramp sequence:
|
||||
acceleration ramp, cruising state, and deceleration ramp. Each ramp's travel distance
|
||||
may range from zero to the length of the block. Velocity profiles can end either at
|
||||
the end of planner block (typical) or mid-block at the end of a forced deceleration,
|
||||
such as from a feed hold.
|
||||
*/
|
||||
float dt_max = DT_SEGMENT; // Maximum segment time
|
||||
float dt = 0.0; // Initialize segment time
|
||||
float time_var = dt_max; // Time worker variable
|
||||
float mm_var; // mm-Distance worker variable
|
||||
float speed_var; // Speed worker variable
|
||||
float mm_remaining = pl_block->millimeters; // New segment distance from end of block.
|
||||
float minimum_mm = mm_remaining-prep.req_mm_increment; // Guarantee at least one step.
|
||||
if (minimum_mm < 0.0) { minimum_mm = 0.0; }
|
||||
|
||||
do {
|
||||
switch (prep.ramp_type) {
|
||||
case RAMP_ACCEL:
|
||||
// NOTE: Acceleration ramp only computes during first do-while loop.
|
||||
speed_var = pl_block->acceleration*time_var;
|
||||
mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var);
|
||||
if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
|
||||
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
|
||||
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
|
||||
time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed);
|
||||
if (mm_remaining == prep.decelerate_after) { prep.ramp_type = RAMP_DECEL; }
|
||||
else { prep.ramp_type = RAMP_CRUISE; }
|
||||
prep.current_speed = prep.maximum_speed;
|
||||
} else { // Acceleration only.
|
||||
prep.current_speed += speed_var;
|
||||
}
|
||||
break;
|
||||
case RAMP_CRUISE:
|
||||
// NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations.
|
||||
// NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To
|
||||
// prevent this, simply enforce a minimum speed threshold in the planner.
|
||||
mm_var = mm_remaining - prep.maximum_speed*time_var;
|
||||
if (mm_var < prep.decelerate_after) { // End of cruise.
|
||||
// Cruise-deceleration junction or end of block.
|
||||
time_var = (mm_remaining - prep.decelerate_after)/prep.maximum_speed;
|
||||
mm_remaining = prep.decelerate_after; // NOTE: 0.0 at EOB
|
||||
prep.ramp_type = RAMP_DECEL;
|
||||
} else { // Cruising only.
|
||||
mm_remaining = mm_var;
|
||||
}
|
||||
break;
|
||||
default: // case RAMP_DECEL:
|
||||
// NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed.
|
||||
speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min)
|
||||
if (prep.current_speed > speed_var) { // Check if at or below zero speed.
|
||||
// Compute distance from end of segment to end of block.
|
||||
mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm)
|
||||
if (mm_var > prep.mm_complete) { // Deceleration only.
|
||||
mm_remaining = mm_var;
|
||||
prep.current_speed -= speed_var;
|
||||
break; // Segment complete. Exit switch-case statement. Continue do-while loop.
|
||||
}
|
||||
} // End of block or end of forced-deceleration.
|
||||
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
|
||||
mm_remaining = prep.mm_complete;
|
||||
}
|
||||
dt += time_var; // Add computed ramp time to total segment time.
|
||||
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
|
||||
else {
|
||||
if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps.
|
||||
// Increase segment time to ensure at least one step in segment. Override and loop
|
||||
// through distance calculations until minimum_mm or mm_complete.
|
||||
dt_max += DT_SEGMENT;
|
||||
time_var = dt_max - dt;
|
||||
} else {
|
||||
break; // **Complete** Exit loop. Segment execution time maxed.
|
||||
}
|
||||
}
|
||||
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------------
|
||||
Compute segment step rate, steps to execute, and apply necessary rate corrections.
|
||||
NOTE: Steps are computed by direct scalar conversion of the millimeter distance
|
||||
remaining in the block, rather than incrementally tallying the steps executed per
|
||||
segment. This helps in removing floating point round-off issues of several additions.
|
||||
However, since floats have only 7.2 significant digits, long moves with extremely
|
||||
high step counts can exceed the precision of floats, which can lead to lost steps.
|
||||
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines
|
||||
supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm).
|
||||
*/
|
||||
float steps_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps
|
||||
float n_steps_remaining = ceil(steps_remaining); // Round-up current steps remaining
|
||||
float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining
|
||||
prep_segment->n_step = last_n_steps_remaining-n_steps_remaining; // Compute number of steps to execute.
|
||||
|
||||
// Bail if we are at the end of a feed hold and don't have a step to execute.
|
||||
if (prep_segment->n_step == 0) {
|
||||
if (sys.state == STATE_HOLD) {
|
||||
|
||||
// Less than one step to decelerate to zero speed, but already very close. AMASS
|
||||
// requires full steps to execute. So, just bail.
|
||||
prep.current_speed = 0.0;
|
||||
prep.dt_remainder = 0.0;
|
||||
prep.steps_remaining = n_steps_remaining;
|
||||
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
|
||||
plan_cycle_reinitialize();
|
||||
sys.state = STATE_QUEUED;
|
||||
return; // Segment not generated, but current step data still retained.
|
||||
}
|
||||
}
|
||||
|
||||
// Compute segment step rate. Since steps are integers and mm distances traveled are not,
|
||||
// the end of every segment can have a partial step of varying magnitudes that are not
|
||||
// executed, because the stepper ISR requires whole steps due to the AMASS algorithm. To
|
||||
// compensate, we track the time to execute the previous segment's partial step and simply
|
||||
// apply it with the partial step distance to the current segment, so that it minutely
|
||||
// adjusts the whole segment rate to keep step output exact. These rate adjustments are
|
||||
// typically very small and do not adversely effect performance, but ensures that Grbl
|
||||
// outputs the exact acceleration and velocity profiles as computed by the planner.
|
||||
dt += prep.dt_remainder; // Apply previous segment partial step execute time
|
||||
float inv_rate = dt/(last_n_steps_remaining - steps_remaining); // Compute adjusted step rate inverse
|
||||
prep.dt_remainder = (n_steps_remaining - steps_remaining)*inv_rate; // Update segment partial step time
|
||||
|
||||
// Compute CPU cycles per step for the prepped segment.
|
||||
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
|
||||
|
||||
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
|
||||
// Compute step timing and multi-axis smoothing level.
|
||||
// NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
|
||||
if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; }
|
||||
else {
|
||||
if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; }
|
||||
else if (cycles < AMASS_LEVEL3) { prep_segment->amass_level = 2; }
|
||||
else { prep_segment->amass_level = 3; }
|
||||
cycles >>= prep_segment->amass_level;
|
||||
prep_segment->n_step <<= prep_segment->amass_level;
|
||||
}
|
||||
if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; } // < 65536 (4.1ms @ 16MHz)
|
||||
else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible.
|
||||
#else
|
||||
// Compute step timing and timer prescalar for normal step generation.
|
||||
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
|
||||
prep_segment->prescaler = 1; // prescaler: 0
|
||||
prep_segment->cycles_per_tick = cycles;
|
||||
} else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz)
|
||||
prep_segment->prescaler = 2; // prescaler: 8
|
||||
prep_segment->cycles_per_tick = cycles >> 3;
|
||||
} else {
|
||||
prep_segment->prescaler = 3; // prescaler: 64
|
||||
if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz)
|
||||
prep_segment->cycles_per_tick = cycles >> 6;
|
||||
} else { // Just set the slowest speed possible. (Around 4 step/sec.)
|
||||
prep_segment->cycles_per_tick = 0xffff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Segment complete! Increment segment buffer indices.
|
||||
segment_buffer_head = segment_next_head;
|
||||
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
|
||||
|
||||
// Setup initial conditions for next segment.
|
||||
if (mm_remaining > prep.mm_complete) {
|
||||
// Normal operation. Block incomplete. Distance remaining in block to be executed.
|
||||
pl_block->millimeters = mm_remaining;
|
||||
prep.steps_remaining = steps_remaining;
|
||||
} else {
|
||||
// End of planner block or forced-termination. No more distance to be executed.
|
||||
if (mm_remaining > 0.0) { // At end of forced-termination.
|
||||
// Reset prep parameters for resuming and then bail.
|
||||
// NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
|
||||
prep.current_speed = 0.0;
|
||||
prep.dt_remainder = 0.0;
|
||||
prep.steps_remaining = ceil(steps_remaining);
|
||||
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
|
||||
plan_cycle_reinitialize();
|
||||
sys.state = STATE_QUEUED; // End cycle.
|
||||
|
||||
return; // Bail!
|
||||
// TODO: Try to move QUEUED setting into cycle re-initialize.
|
||||
|
||||
} else { // End of planner block
|
||||
// The planner block is complete. All steps are set to be executed in the segment buffer.
|
||||
pl_block = NULL;
|
||||
plan_discard_current_block();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called by realtime status reporting to fetch the current speed being executed. This value
|
||||
// however is not exactly the current speed, but the speed computed in the last step segment
|
||||
// in the segment buffer. It will always be behind by up to the number of segment blocks (-1)
|
||||
// divided by the ACCELERATION TICKS PER SECOND in seconds.
|
||||
#ifdef REPORT_REALTIME_RATE
|
||||
float st_get_realtime_rate()
|
||||
{
|
||||
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD)){
|
||||
return prep.current_speed;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
#endif
|
60
grbl/stepper.h
Normal file
60
grbl/stepper.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2012-2015 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/>.
|
||||
*/
|
||||
/*
|
||||
This file is based on work from Grbl v0.8, distributed under the
|
||||
terms of the MIT-license. See COPYING for more details.
|
||||
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
||||
Copyright (c) 2011 Sungeun K. Jeon
|
||||
*/
|
||||
|
||||
#ifndef stepper_h
|
||||
#define stepper_h
|
||||
|
||||
#ifndef SEGMENT_BUFFER_SIZE
|
||||
#define SEGMENT_BUFFER_SIZE 6
|
||||
#endif
|
||||
|
||||
// Initialize and setup the stepper motor subsystem
|
||||
void stepper_init();
|
||||
|
||||
// Enable steppers, but cycle does not start unless called by motion control or realtime command.
|
||||
void st_wake_up();
|
||||
|
||||
// Immediately disables steppers
|
||||
void st_go_idle();
|
||||
|
||||
// Generate the step and direction port invert masks.
|
||||
void st_generate_step_dir_invert_masks();
|
||||
|
||||
// Reset the stepper subsystem variables
|
||||
void st_reset();
|
||||
|
||||
// Reloads step segment buffer. Called continuously by realtime execution system.
|
||||
void st_prep_buffer();
|
||||
|
||||
// Called by planner_recalculate() when the executing block is updated by the new plan.
|
||||
void st_update_plan_block_parameters();
|
||||
|
||||
// Called by realtime status reporting if realtime rate reporting is enabled in config.h.
|
||||
#ifdef REPORT_REALTIME_RATE
|
||||
float st_get_realtime_rate();
|
||||
#endif
|
||||
|
||||
#endif
|
236
grbl/system.c
Normal file
236
grbl/system.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
system.c - Handles system level commands and real-time processes
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2014-2015 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 "grbl.h"
|
||||
|
||||
|
||||
void system_init()
|
||||
{
|
||||
CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins
|
||||
#ifdef DISABLE_CONTROL_PIN_PULL_UP
|
||||
CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down.
|
||||
#else
|
||||
CONTROL_PORT |= CONTROL_MASK; // Enable internal pull-up resistors. Normal high operation.
|
||||
#endif
|
||||
CONTROL_PCMSK |= CONTROL_MASK; // Enable specific pins of the Pin Change Interrupt
|
||||
PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt
|
||||
}
|
||||
|
||||
|
||||
// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
|
||||
// only the realtime command execute variable to have the main program execute these when
|
||||
// its ready. This works exactly like the character-based realtime commands when picked off
|
||||
// directly from the incoming serial data stream.
|
||||
ISR(CONTROL_INT_vect)
|
||||
{
|
||||
uint8_t pin = (CONTROL_PIN & CONTROL_MASK);
|
||||
#ifndef INVERT_CONTROL_PIN
|
||||
pin ^= CONTROL_MASK;
|
||||
#endif
|
||||
// Enter only if any CONTROL pin is detected as active.
|
||||
if (pin) {
|
||||
if (bit_istrue(pin,bit(RESET_BIT))) {
|
||||
mc_reset();
|
||||
} else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) {
|
||||
bit_true(sys.rt_exec_state, EXEC_FEED_HOLD);
|
||||
} else if (bit_istrue(pin,bit(CYCLE_START_BIT))) {
|
||||
bit_true(sys.rt_exec_state, 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 realtime command module by being susceptible to when Grbl is ready to execute the
|
||||
// next line during a cycle, so for switches like block delete, the switch only effects
|
||||
// the lines that are processed afterward, not necessarily real-time during a cycle,
|
||||
// since there are motions already stored in the buffer. However, this 'lag' should not
|
||||
// be an issue, since these commands are not typically used during a cycle.
|
||||
uint8_t system_execute_line(char *line)
|
||||
{
|
||||
uint8_t char_counter = 1;
|
||||
uint8_t helper_var = 0; // Helper variable
|
||||
float parameter, value;
|
||||
switch( line[char_counter] ) {
|
||||
case 0 : report_grbl_help(); break;
|
||||
case '$' : // Prints Grbl settings
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
|
||||
if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print.
|
||||
else { report_grbl_settings(); }
|
||||
break;
|
||||
case 'G' : // Prints gcode parser state
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
|
||||
else { report_gcode_modes(); }
|
||||
break;
|
||||
case 'C' : // Set check g-code mode [IDLE/CHECK]
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
|
||||
// Perform reset when toggling off. Check g-code mode should only work if Grbl
|
||||
// is idle and ready, regardless of alarm locks. This is mainly to keep things
|
||||
// simple and consistent.
|
||||
if ( sys.state == STATE_CHECK_MODE ) {
|
||||
mc_reset();
|
||||
report_feedback_message(MESSAGE_DISABLED);
|
||||
} else {
|
||||
if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode.
|
||||
sys.state = STATE_CHECK_MODE;
|
||||
report_feedback_message(MESSAGE_ENABLED);
|
||||
}
|
||||
break;
|
||||
case 'X' : // Disable alarm lock [ALARM]
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
|
||||
if (sys.state == STATE_ALARM) {
|
||||
report_feedback_message(MESSAGE_ALARM_UNLOCK);
|
||||
sys.state = STATE_IDLE;
|
||||
// Don't run startup script. Prevents stored moves in startup from causing accidents.
|
||||
} // Otherwise, no effect.
|
||||
break;
|
||||
// case 'J' : break; // Jogging methods
|
||||
// TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
|
||||
// susceptible to other realtime commands except for e-stop. The jogging function is intended to
|
||||
// be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
|
||||
// steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
|
||||
// start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
|
||||
// motion by repeatedly toggling to slow the motion to the desired location. Location data would
|
||||
// need to be updated real-time and supplied to the user through status queries.
|
||||
// More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
|
||||
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the
|
||||
// block buffer without having the planner plan them. It would need to manage de/ac-celerations
|
||||
// on its own carefully. This approach could be effective and possibly size/memory efficient.
|
||||
default :
|
||||
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
|
||||
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
|
||||
switch( line[char_counter] ) {
|
||||
case '#' : // Print Grbl NGC parameters
|
||||
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
|
||||
else { report_ngc_parameters(); }
|
||||
break;
|
||||
case 'H' : // Perform homing cycle [IDLE/ALARM]
|
||||
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
|
||||
sys.state = STATE_HOMING; // Set system state variable
|
||||
// Only perform homing if Grbl is idle or lost.
|
||||
mc_homing_cycle();
|
||||
if (!sys.abort) { // Execute startup scripts after successful homing.
|
||||
sys.state = STATE_IDLE; // Set to IDLE when complete.
|
||||
st_go_idle(); // Set steppers to the settings idle state before returning.
|
||||
system_execute_startup(line);
|
||||
}
|
||||
} else { return(STATUS_SETTING_DISABLED); }
|
||||
break;
|
||||
case 'I' : // Print or store build info. [IDLE/ALARM]
|
||||
if ( line[++char_counter] == 0 ) {
|
||||
settings_read_build_info(line);
|
||||
report_build_info(line);
|
||||
} else { // Store startup line [IDLE/ALARM]
|
||||
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
|
||||
helper_var = char_counter; // Set helper variable as counter to start of user info line.
|
||||
do {
|
||||
line[char_counter-helper_var] = line[char_counter];
|
||||
} while (line[char_counter++] != 0);
|
||||
settings_store_build_info(line);
|
||||
}
|
||||
break;
|
||||
case 'N' : // Startup lines. [IDLE/ALARM]
|
||||
if ( line[++char_counter] == 0 ) { // Print startup lines
|
||||
for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
|
||||
if (!(settings_read_startup_line(helper_var, line))) {
|
||||
report_status_message(STATUS_SETTING_READ_FAIL);
|
||||
} else {
|
||||
report_startup_line(helper_var,line);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else { // Store startup line [IDLE Only] Prevents motion during ALARM.
|
||||
if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle.
|
||||
helper_var = true; // Set helper_var to flag storing method.
|
||||
// No break. Continues into default: to read remaining command characters.
|
||||
}
|
||||
default : // Storing setting methods [IDLE/ALARM]
|
||||
if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
|
||||
if (helper_var) { // Store startup line
|
||||
// Prepare sending gcode block to gcode parser by shifting all characters
|
||||
helper_var = char_counter; // Set helper variable as counter to start of gcode block
|
||||
do {
|
||||
line[char_counter-helper_var] = line[char_counter];
|
||||
} while (line[char_counter++] != 0);
|
||||
// Execute gcode block to ensure block is valid.
|
||||
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
|
||||
if (helper_var) { return(helper_var); }
|
||||
else {
|
||||
helper_var = trunc(parameter); // Set helper_var to int value of parameter
|
||||
settings_store_startup_line(helper_var,line);
|
||||
}
|
||||
} else { // Store global setting.
|
||||
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); }
|
||||
return(settings_store_global_setting((uint8_t)parameter, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
|
||||
}
|
||||
|
||||
|
||||
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
|
||||
// NOTE: If motor steps and machine position are not in the same coordinate frame, this function
|
||||
// serves as a central place to compute the transformation.
|
||||
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
|
||||
{
|
||||
float pos;
|
||||
#ifdef COREXY
|
||||
if (idx==A_MOTOR) {
|
||||
pos = 0.5*((steps[A_MOTOR] + steps[B_MOTOR])/settings.steps_per_mm[idx]);
|
||||
} else { // (idx==B_MOTOR)
|
||||
pos = 0.5*((steps[A_MOTOR] - steps[B_MOTOR])/settings.steps_per_mm[idx]);
|
||||
}
|
||||
#else
|
||||
pos = steps[idx]/settings.steps_per_mm[idx];
|
||||
#endif
|
||||
return(pos);
|
||||
}
|
||||
|
||||
|
||||
void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
|
||||
{
|
||||
uint8_t idx;
|
||||
for (idx=0; idx<N_AXIS; idx++) {
|
||||
position[idx] = system_convert_axis_steps_to_mpos(steps, idx);
|
||||
}
|
||||
return;
|
||||
}
|
95
grbl/system.h
Normal file
95
grbl/system.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
system.h - Header for system level commands and real-time processes
|
||||
Part of Grbl v0.9
|
||||
|
||||
Copyright (c) 2014-2015 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
|
||||
|
||||
#include "grbl.h"
|
||||
|
||||
// Define system executor bit map. Used internally by realtime protocol as realtime command flags,
|
||||
// which notifies the main program to execute the specified realtime command asynchronously.
|
||||
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
|
||||
// flags are always false, so the realtime protocol only needs to check for a non-zero value to
|
||||
// know when there is a realtime command to execute.
|
||||
#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001
|
||||
#define EXEC_CYCLE_START bit(1) // bitmask 00000010
|
||||
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
|
||||
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000
|
||||
#define EXEC_RESET bit(4) // bitmask 00010000
|
||||
|
||||
// Alarm executor bit map.
|
||||
// NOTE: EXEC_CRITICAL_EVENT is an optional flag that must be set with an alarm flag. When enabled,
|
||||
// this halts Grbl into an infinite loop until the user aknowledges the problem and issues a soft-
|
||||
// reset command. For example, a hard limit event needs this type of halt and aknowledgement.
|
||||
#define EXEC_CRITICAL_EVENT bit(0) // bitmask 00000001 (SPECIAL FLAG. See NOTE:)
|
||||
#define EXEC_ALARM_HARD_LIMIT bit(0) // bitmask 00000010
|
||||
#define EXEC_ALARM_SOFT_LIMIT bit(1) // bitmask 00000100
|
||||
#define EXEC_ALARM_ABORT_CYCLE bit(2) // bitmask 00001000
|
||||
#define EXEC_ALARM_PROBE_FAIL bit(3) // bitmask 00010000
|
||||
|
||||
// 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 rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
|
||||
volatile uint8_t rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
|
||||
|
||||
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.
|
||||
|
||||
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
|
||||
volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
|
||||
int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
|
||||
uint8_t probe_succeeded; // Tracks if last probing cycle was successful.
|
||||
} 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);
|
||||
|
||||
// Execute the startup script lines stored in EEPROM upon initialization
|
||||
void system_execute_startup(char *line);
|
||||
|
||||
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
|
||||
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx);
|
||||
|
||||
// Updates a machine 'position' array based on the 'step' array sent.
|
||||
void system_convert_array_steps_to_mpos(float *position, int32_t *steps);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user