v1.0 Beta Release.

- Tons of new stuff in this release, which is fairly stable and well
tested. However, much more is coming soon!

- Real-time parking motion with safety door. When this compile option
is enabled, an opened safety door will cause Grbl to automatically feed
hold, retract, de-energize the spindle/coolant, and parks near Z max.
After the door is closed and resume is commanded, this reverses and the
program continues as if nothing happened. This is also highly
configurable. See config.h for details.

- New spindle max and min rpm ‘$’ settings! This has been requested
often. Grbl will output 5V when commanded to turn on the spindle at its
max rpm, and 0.02V with min rpm. The voltage and the rpm range are
linear to each other. This should help users tweak their settings to
get close to true rpm’s.

- If the new max rpm ‘$’ setting is set = 0 or less than min rpm, the
spindle speed PWM pin will act like a regular on/off spindle enable
pin. On pin D11.

- BEWARE: Your old EEPROM settings will be wiped! The new spindle rpm
settings require a new settings version, so Grbl will automatically
wipe and restore the EEPROM with the new defaults.

- Control pin can now be inverted individually with a
CONTROL_INVERT_MASK in the cpu_map header file. Not typical for users
to need this, but handy to have.

- Fixed bug when Grbl receive too many characters in a line and
overflows. Previously it would respond with an error per overflow
character and another acknowledge upon an EOL character. This broke the
streaming protocol. Now fixed to only respond with an error after an
EOL character.

- Fixed a bug with the safety door during an ALARM mode. You now can’t
home or unlock the axes until the safety door has been closed. This is
for safety reasons (obviously.)

- Tweaked some the Mega2560 cpu_map settings . Increased segment buffer
size and fixed the spindle PWM settings to output at a higher PWM
frequency.

- Generalized the delay function used by G4 delay for use by parking
motion. Allows non-blocking status reports and real-time control during
re-energizing of the spindle and coolant.

- Added spindle rpm max and min defaults to default.h files.

- Added a new print float for rpm values.
This commit is contained in:
Sonny Jeon 2015-08-27 21:37:19 -06:00
parent 3a68c22fab
commit b3a53a4683
36 changed files with 972 additions and 598 deletions

View File

@ -1,6 +1,10 @@
![GitHub Logo](/doc/media/Grbl Logo 250px.png) ![GitHub Logo](/doc/media/Grbl Logo 250px.png)
***
This is the development branch for Grbl v1.0's upcoming release. In general, the new features here are beta, so use with caution. If you'd like to help, please report any bugs or oddities that you find! Thanks!
*** ***
Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328. Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328.
@ -24,67 +28,21 @@ Grbl includes full acceleration management with look ahead. That means the contr
### Official Supporters of the Grbl CNC Project ### Official Supporters of the Grbl CNC Project
[![Carbide3D](http://carbide3d.com/files/logo_240px.png)](http://carbide3d.com) [![Inventables](https://dzevsq2emy08i.cloudfront.net/paperclip/press_image_uploads/62/low_res/inventables-logo.png)](http://inventables.com) [![Carbide3D](http://carbide3d.com/files/logo_240px.png)](http://carbide3d.com) [![Inventables](https://dzevsq2emy08i.cloudfront.net/paperclip/press_image_uploads/62/low_res/inventables-logo.png)](http://inventables.com)
***
_**Master Branch:**_
* [Grbl v0.9j Atmega328p 16mhz 115200baud with generic defaults](http://bit.ly/1I8Ey4S) _(2015-07-17)_
* [Grbl v0.9j Atmega328p 16mhz 115200baud with ShapeOko2 defaults](http://bit.ly/1OjUSia) _(2015-07-17)_
- **IMPORTANT INFO WHEN UPGRADING TO GRBL v0.9 :**
- Baudrate is now **115200** (Up from 9600).
- Homing cycle updated. Located based on switch trigger, rather than release point.
- Variable spindle is now enabled by default. Z-limit(D12) and spindle enable(D11) have switched to access the hardware PWM on D11. Homing will not work if you do not re-wire your Z-limit switch to D12.
_**Archives:**_
* [Grbl v0.9i Atmega328p 16mhz 115200baud with generic defaults](http://bit.ly/1EiviDk)
* [Grbl v0.9i Atmega328p 16mhz 115200baud with ShapeOko2 defaults](http://bit.ly/1NYIfKl)
* [Grbl v0.9g Atmega328p 16mhz 115200baud with generic defaults](http://bit.ly/1m8E1Qa)
* [Grbl v0.9g Atmega328p 16mhz 115200baud with ShapeOko2 defaults](http://bit.ly/1kOAzig)
* [Grbl v0.8c Atmega328p 16mhz 9600baud](http://bit.ly/SSdCJE)
* [Grbl v0.7d Atmega328p 16mhz 9600baud](http://bit.ly/ZhL15G)
* [Grbl v0.6b Atmega328p 16mhz 9600baud](http://bit.ly/VD04A5)
* [Grbl v0.51 Atmega328p 16mhz 9600baud](http://bit.ly/W75BS1)
* [Grbl v0.6b Atmega168 16mhz 9600baud](http://bit.ly/SScWnE)
* [Grbl v0.51 Atmega168 16mhz 9600baud](http://bit.ly/VXyrYu)
*** ***
##Update Summary for v0.9j ##Update Summary for v1.0b
- **Restore EEPROM feature:** A new set of restore EEPROM features to help OEMs and users reset their Grbl installation to the build defaults. See Configuring Grbl Wiki for details. - **IMPORTANT:** Your EEPROM will be wiped and restored with new settings. This is due to the addition of two new spindle speed '$' settings.
##Update Summary for v0.9i - New safety door parking motion as a compile-option. Grbl will retract, disable the spindle/coolant, and park near Z max. When resumed, it will perform these task in reverse order and continue the program. Highly configurable. See config.h for details.
- **IMPORTANT:**
- **Homing cycle updated. Locates based on trigger point, rather than release point.** - New '$' Grbl settings for max and min spindle rpm. Allows for tweaking the PWM output to more closely match true spindle rpm. When max rpm is set to zero or less than min rpm, the PWM pin D11 will act like a simple enable on/off output.
- **System tweaks: $14 cycle auto-start has been removed. No more QUEUE state.**
- **New G-Codes** - A few bug fixes and lots of refactoring to make the code more efficient and flexible.
- **CoreXY Support**
- **Safety Door Support**
- **Full Limit and Control Pin Configurability**
- **Additional Compile-Time Feature Options**
##Update Summary for v0.9h from v0.8
- **IMPORTANT:**
- **Default serial baudrate is now 115200! (Up from 9600)**
- **Z-limit(D12) and spindle enable(D11) pins have switched to support variable spindle!**
- **Super Smooth Stepper Algorithm**
- **Stability and Robustness Updates**
- **(x4)+ Faster Planner**
- **Compile-able via Arduino IDE!**
- **G-Code Parser Overhaul**
- **Independent Acceleration and Velocity Settings**
- **Soft Limits**
- **Probing**
- **Dynamic Tool Length Offsets**
- **Improved Arc Performance**
- **CPU Pin Mapping**
- **New Grbl SIMULATOR! (by @jgeisler and @ashelly)**
- **Configurable Real-time Status Reporting**
- **Updated Homing Routine**
- **Optional Limit Pin Sharing**
- **Optional Variable Spindle Speed Output**
- **Additional Compile-Time Feature Options**
- -
``` ```
List of Supported G-Codes in Grbl v0.9 Master: List of Supported G-Codes in Grbl v0.9 Master:
- Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G92, G92.1 - Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G92, G92.1

View File

@ -102,6 +102,7 @@
#define N_DECIMAL_RATEVALUE_INCH 1 // Rate or velocity value in in/min #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_RATEVALUE_MM 0 // Rate or velocity value in mm/min
#define N_DECIMAL_SETTINGVALUE 3 // Decimals for floating point setting values #define N_DECIMAL_SETTINGVALUE 3 // Decimals for floating point setting values
#define N_DECIMAL_RPMVALUE 0 // RPM value in rotations per min.
// If your machine has two limits switches wired in parallel to one axis, you will need to enable // 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 // this feature. Since the two switches are sharing a single pin, there is no way for Grbl to tell
@ -138,9 +139,8 @@
// After the safety door switch has been toggled and restored, this setting sets the power-up delay // After the safety door switch has been toggled and restored, this setting sets the power-up delay
// between restoring the spindle and coolant and resuming the cycle. // between restoring the spindle and coolant and resuming the cycle.
// NOTE: Delay value is defined in milliseconds from zero to 65,535. #define SAFETY_DOOR_SPINDLE_DELAY 4.0 // Float (seconds)
#define SAFETY_DOOR_SPINDLE_DELAY 4000 #define SAFETY_DOOR_COOLANT_DELAY 1.0 // Float (seconds)
#define SAFETY_DOOR_COOLANT_DELAY 1000
// Enable CoreXY kinematics. Use ONLY with CoreXY machines. // Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to // IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
@ -238,19 +238,14 @@
// The hardware PWM output on pin D11 is required for variable spindle output voltages. // The hardware PWM output on pin D11 is required for variable spindle output voltages.
#define VARIABLE_SPINDLE // Default enabled. Comment to disable. #define VARIABLE_SPINDLE // Default enabled. Comment to disable.
// 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. // 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 // The PWM pin will still read 0V when the spindle is disabled. Most users will not need this option, but
// useful in certain scenarios. This setting does not update the minimum spindle RPM calculations. Any // it may be useful in certain scenarios. This minimum PWM settings coincides with the spindle rpm minimum
// spindle RPM output lower than this value will be set to this value. // setting, like rpm max to max PWM. So the variable spindle pin will not output the voltage range between
// #define MINIMUM_SPINDLE_PWM 5 // Default disabled. Uncomment to enable. Integer (0-255) // 0V for disabled and the voltage set by the minimum PWM for minimum rpm.
// NOTE: Compute duty cycle at the minimum PWM by this equation: (% duty cycle)=(SPINDLE_MINIMUM_PWM/256)*100
// #define SPINDLE_MINIMUM_PWM 5 // Default disabled. Uncomment to enable. Integer (0-255)
// By default on a 328p(Uno), Grbl combines the variable spindle PWM and the enable into one pin to help // By default on a 328p(Uno), Grbl combines the variable spindle PWM and the enable into one pin to help
// preserve I/O pins. For certain setups, these may need to be separate pins. This configure option uses // preserve I/O pins. For certain setups, these may need to be separate pins. This configure option uses
@ -383,6 +378,40 @@
// NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled. // NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled.
// #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable. // #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable.
// Adjusts homing cycle search and locate scalars. These are the multipliers used by Grbl's
// homing cycle to ensure the limit switches are engaged and cleared through each phase of
// the cycle. The search phase uses the axes max-travel setting times the SEARCH_SCALAR to
// determine distance to look for the limit switch. Once found, the locate phase begins and
// uses the homing pull-off distance setting times the LOCATE_SCALAR to pull-off and re-engage
// the limit switch.
// NOTE: Both of these values must be greater than 1.0 to ensure proper function.
// #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Uncomment to override defaults in limits.c.
// #define HOMING_AXIS_LOCATE_SCALAR 10.0 // Uncomment to override defaults in limits.c.
// Enables and configures parking motion methods upon a safety door state. Primarily for OEMs
// that desire this feature for their integrated machines. At the moment, Grbl assumes that
// the parking motion only involves one axis, although the parking implementation was written
// to be easily refactored for any number of motions on different axes by altering the parking
// source code. At this time, Grbl only supports parking one axis (typically the Z-axis) that
// moves in the positive direction upon retracting and negative direction upon restoring position.
// The motion executes with a slow pull-out retraction motion, power-down, and a fast park.
// Restoring to the resume position follows these set motions in reverse: fast restore to
// pull-out position, power-up with a time-out, and plunge back to the original position at the
// slower pull-out rate.
// NOTE: Still a work-in-progress. Machine coordinates must be in all negative space and
// does not work with HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
// positive direction.
// #define PARKING_ENABLE // Default disabled. Uncomment to enable
// Configure options for the parking motion, if enabled.
#define PARKING_AXIS Z_AXIS // Define which axis that performs the parking motion
#define PARKING_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
#define PARKING_RATE -1.0 // Parking fast rate after pull-out. In mm/min or (-1.0) for seek rate.
#define PARKING_PULLOUT_RATE 250.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
// Must be positive value or equal to zero.
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES: // COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
@ -399,6 +428,12 @@
#error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with a 328p processor" #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with a 328p processor"
#endif #endif
#if defined(PARKING_ENABLE)
#if defined(HOMING_FORCE_SET_ORIGIN)
#error "HOMING_FORCE_SET_ORIGIN is not supported with PARKING_ENABLE at this time."
#endif
#endif
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------

View File

@ -42,6 +42,8 @@ void coolant_stop()
void coolant_set_state(uint8_t mode) void coolant_set_state(uint8_t mode)
{ {
if (sys.abort) { return; } // Block during abort.
if (mode == COOLANT_FLOOD_ENABLE) { if (mode == COOLANT_FLOOD_ENABLE) {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);

View File

@ -33,10 +33,11 @@
#define SERIAL_UDRE USART0_UDRE_vect #define SERIAL_UDRE USART0_UDRE_vect
// Increase Buffers to make use of extra SRAM // Increase Buffers to make use of extra SRAM
//#define RX_BUFFER_SIZE 256 //#define RX_BUFFER_SIZE 256
//#define TX_BUFFER_SIZE 128 //#define TX_BUFFER_SIZE 128
//#define BLOCK_BUFFER_SIZE 36 //#define BLOCK_BUFFER_SIZE 36
//#define LINE_BUFFER_SIZE 100 //#define LINE_BUFFER_SIZE 100
//#define SEGMENT_BUFFER_SIZE 10
// Define step pulse output pins. NOTE: All step bit pins must be on the same port. // Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEP_DDR DDRA #define STEP_DDR DDRA
@ -107,8 +108,8 @@
#define CONTROL_INT PCIE2 // Pin change interrupt enable pin #define CONTROL_INT PCIE2 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT2_vect #define CONTROL_INT_vect PCINT2_vect
#define CONTROL_PCMSK PCMSK2 // Pin change interrupt register #define CONTROL_PCMSK PCMSK2 // Pin change interrupt register
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT)) #define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins. #define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin. // Define probe switch input pin.
#define PROBE_DDR DDRK #define PROBE_DDR DDRK
@ -121,18 +122,19 @@
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables // Advanced Configuration Below You should not need to touch these variables
// Set Timer up to use TIMER4B which is attached to Digital Pin 7 // Set Timer up to use TIMER4B which is attached to Digital Pin 7
#define PWM_MAX_VALUE 65535.0 #define PWM_MAX_VALUE 1024.0 // Translates to about 1.9 kHz PWM frequency at 1/8 prescaler
#define TCCRA_REGISTER TCCR4A #define TCCRA_REGISTER TCCR4A
#define TCCRB_REGISTER TCCR4B #define TCCRB_REGISTER TCCR4B
#define OCR_REGISTER OCR4B #define OCR_REGISTER OCR4B
#define COMB_BIT COM4B1
#define COMB_BIT COM4B1 // 1/8 Prescaler, 16-bit Fast PWM mode
#define WAVE0_REGISTER WGM40 #define TCCRA_INIT_MASK ((1<<WGM40) | (1<<WGM41))
#define WAVE1_REGISTER WGM41 #define TCCRB_INIT_MASK ((1<<WGM42) | (1<<WGM43) | (1<<CS41))
#define WAVE2_REGISTER WGM42 #define OCRA_REGISTER OCR4A // 16-bit Fast PWM mode requires top reset value stored here.
#define WAVE3_REGISTER WGM43 #define OCRA_TOP_VALUE 0x0400 // PWM counter reset value. Should be the same as PWM_MAX_VALUE in hex.
#define SPINDLE_PWM_DDR DDRH #define SPINDLE_PWM_DDR DDRH
#define SPINDLE_PWM_PORT PORTH #define SPINDLE_PWM_PORT PORTH
#define SPINDLE_PWM_BIT 4 // MEGA2560 Digital Pin 97 #define SPINDLE_PWM_BIT 4 // MEGA2560 Digital Pin 7
#endif // End of VARIABLE_SPINDLE #endif // End of VARIABLE_SPINDLE

View File

@ -116,8 +116,8 @@
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin #define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT1_vect #define CONTROL_INT_vect PCINT1_vect
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register #define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT)) #define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins. #define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin. // Define probe switch input pin.
#define PROBE_DDR DDRC #define PROBE_DDR DDRC
@ -129,16 +129,15 @@
// Start of PWM & Stepper Enabled Spindle // Start of PWM & Stepper Enabled Spindle
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables // Advanced Configuration Below You should not need to touch these variables
#define PWM_MAX_VALUE 255.0 #define PWM_MAX_VALUE 255.0 // Don't change. 328p fast PWM mode fixes top value as 255.
#define TCCRA_REGISTER TCCR2A #define TCCRA_REGISTER TCCR2A
#define TCCRB_REGISTER TCCR2B #define TCCRB_REGISTER TCCR2B
#define OCR_REGISTER OCR2A #define OCR_REGISTER OCR2A
#define COMB_BIT COM2A1
#define COMB_BIT COM2A1 // 1/8 Prescaler, 8-bit Fast PWM mode. Translates to about 7.8kHz PWM frequency.
#define WAVE0_REGISTER WGM20 #define TCCRA_INIT_MASK ((1<<WGM20) | (1<<WGM21))
#define WAVE1_REGISTER WGM21 #define TCCRB_INIT_MASK (1<<CS21)
#define WAVE2_REGISTER WGM22
#define WAVE3_REGISTER WGM23
// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings. // NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#define SPINDLE_PWM_DDR DDRB #define SPINDLE_PWM_DDR DDRB

View File

@ -40,6 +40,8 @@
#define DEFAULT_X_MAX_TRAVEL 200.0 // mm #define DEFAULT_X_MAX_TRAVEL 200.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 200.0 // mm #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0

View File

@ -41,6 +41,8 @@
#define DEFAULT_X_MAX_TRAVEL 500.0 // mm #define DEFAULT_X_MAX_TRAVEL 500.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 750.0 // mm #define DEFAULT_Y_MAX_TRAVEL 750.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 80.0 // mm #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0

View File

@ -48,6 +48,8 @@
#define DEFAULT_X_MAX_TRAVEL 200.0 // mm #define DEFAULT_X_MAX_TRAVEL 200.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 200.0 // mm #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))

View File

@ -47,6 +47,8 @@
#define DEFAULT_X_MAX_TRAVEL 290.0 // mm #define DEFAULT_X_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 290.0 // mm #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Z_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Z_AXIS))

View File

@ -46,6 +46,8 @@
#define DEFAULT_X_MAX_TRAVEL 425.0 // mm #define DEFAULT_X_MAX_TRAVEL 425.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 465.0 // mm #define DEFAULT_Y_MAX_TRAVEL 465.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 80.0 // mm #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Z_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Z_AXIS))

View File

@ -44,6 +44,8 @@
#define DEFAULT_X_MAX_TRAVEL 225.0 // mm #define DEFAULT_X_MAX_TRAVEL 225.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 125.0 // mm #define DEFAULT_Y_MAX_TRAVEL 125.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 170.0 // mm #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 2800.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)|(1<<Z_AXIS))

View File

@ -41,6 +41,8 @@
#define DEFAULT_X_MAX_TRAVEL 1000.0 // mm #define DEFAULT_X_MAX_TRAVEL 1000.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 1000.0 // mm #define DEFAULT_Y_MAX_TRAVEL 1000.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 1000.0 // mm #define DEFAULT_Z_MAX_TRAVEL 1000.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0

View File

@ -47,6 +47,8 @@
#define DEFAULT_X_MAX_TRAVEL 740.0 // mm #define DEFAULT_X_MAX_TRAVEL 740.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 790.0 // mm #define DEFAULT_Y_MAX_TRAVEL 790.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_AXIS))

View File

@ -47,6 +47,8 @@
#define DEFAULT_X_MAX_TRAVEL 290.0 // mm #define DEFAULT_X_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 290.0 // mm #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<X_AXIS)|(1<<Y_AXIS))

View File

@ -45,6 +45,8 @@
#define DEFAULT_X_MAX_TRAVEL 190.0 // mm #define DEFAULT_X_MAX_TRAVEL 190.0 // mm
#define DEFAULT_Y_MAX_TRAVEL 180.0 // mm #define DEFAULT_Y_MAX_TRAVEL 180.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 150.0 // mm #define DEFAULT_Z_MAX_TRAVEL 150.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 10000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEP_PULSE_MICROSECONDS 10
#define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS)) #define DEFAULT_DIRECTION_INVERT_MASK ((1<<Y_AXIS))

View File

@ -22,8 +22,8 @@
#define grbl_h #define grbl_h
// Grbl versioning system // Grbl versioning system
#define GRBL_VERSION "0.9j" #define GRBL_VERSION "1.0b"
#define GRBL_VERSION_BUILD "20150811" #define GRBL_VERSION_BUILD "20150824"
// Define standard libraries used by Grbl. // Define standard libraries used by Grbl.
#include <avr/io.h> #include <avr/io.h>

View File

@ -203,9 +203,9 @@ void limits_go_home(uint8_t cycle_mask)
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
plan_buffer_line(target, homing_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion. plan_buffer_line(target, homing_rate, false, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion.
#else #else
plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion. plan_buffer_line(target, homing_rate, false, false); // Bypass mc_line(). Directly plan homing motion.
#endif #endif
st_prep_buffer(); // Prep and fill segment buffer from newly planned block. st_prep_buffer(); // Prep and fill segment buffer from newly planned block.

View File

@ -67,10 +67,11 @@
} while (1); } while (1);
// Plan and queue motion into planner buffer // Plan and queue motion into planner buffer
// uint8_t plan_status; // Not used in normal operation.
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
plan_buffer_line(target, feed_rate, invert_feed_rate, line_number); plan_buffer_line(target, feed_rate, invert_feed_rate, false, line_number);
#else #else
plan_buffer_line(target, feed_rate, invert_feed_rate); plan_buffer_line(target, feed_rate, invert_feed_rate, false);
#endif #endif
} }
@ -202,17 +203,9 @@
// Execute dwell in seconds. // Execute dwell in seconds.
void mc_dwell(float seconds) void mc_dwell(float seconds)
{ {
if (sys.state == STATE_CHECK_MODE) { return; } if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize();
uint16_t i = floor(1000/DWELL_TIME_STEP*seconds); delay_sec(seconds, DELAY_MODE_DWELL);
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
}
} }
@ -334,6 +327,32 @@ void mc_homing_cycle()
} }
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
// NOTE: Uses the always free planner ring buffer head to store motion parameters for execution.
void mc_parking_motion(float *parking_target, float feed_rate)
{
if (sys.abort) { return; } // Block during abort.
uint8_t plan_status = plan_buffer_line(parking_target, feed_rate, false, true);
if (plan_status) {
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_PARK);
bit_false(sys.step_control, STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case
st_prep_buffer();
st_wake_up();
do {
protocol_exec_rt_system();
if (sys.abort) { return; }
} while (sys.step_control & STEP_CONTROL_EXECUTE_PARK);
st_parking_restore_buffer(); // Restore step segment buffer to normal run state.
} else {
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_PARK);
protocol_exec_rt_system();
}
}
// Method to ready the system to reset by setting the realtime reset command and killing any // 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 // 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 // is in a motion state. If so, kills the steppers and sets the system alarm to flag position
@ -353,7 +372,8 @@ void mc_reset()
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps // 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 // the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off. // violated, by which, all bets are off.
if ((sys.state & (STATE_CYCLE | STATE_HOMING)) || (sys.suspend == SUSPEND_ENABLE_HOLD)) { if ((sys.state & (STATE_CYCLE | STATE_HOMING)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK))) {
if (sys.state == STATE_HOMING) { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_HOMING_FAIL); } if (sys.state == STATE_HOMING) { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_HOMING_FAIL); }
else { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE); } else { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE); }
st_go_idle(); // Force kill steppers. Position has likely been lost. st_go_idle(); // Force kill steppers. Position has likely been lost.

View File

@ -61,6 +61,9 @@ void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, ui
uint8_t is_no_error); uint8_t is_no_error);
#endif #endif
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
void mc_parking_motion(float *parking_target, float feed_rate);
// Performs system reset. If in motion state, kills all motion and sets system alarm. // Performs system reset. If in motion state, kills all motion and sets system alarm.
void mc_reset(); void mc_reset();

View File

@ -108,6 +108,24 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
} }
// Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode)
{
uint16_t i = ceil(1000/DWELL_TIME_STEP*seconds);
while (i-- > 0) {
if (sys.abort) { return; }
if (mode == DELAY_MODE_DWELL) {
protocol_execute_realtime();
} else { // DELAY_MODE_SAFETY_DOOR
// Execute rt_system() only to avoid nesting suspend loops.
protocol_exec_rt_system();
if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens.
}
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
}
}
// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), // Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(),
// which only accepts constants in future compiler releases. // which only accepts constants in future compiler releases.
void delay_ms(uint16_t ms) void delay_ms(uint16_t ms)

View File

@ -44,6 +44,9 @@
#define INCH_PER_MM (0.0393701) #define INCH_PER_MM (0.0393701)
#define TICKS_PER_MICROSECOND (F_CPU/1000000) #define TICKS_PER_MICROSECOND (F_CPU/1000000)
#define DELAY_MODE_DWELL 0
#define DELAY_MODE_SAFETY_DOOR 1
// Useful macros // Useful macros
#define clear_vector(a) memset(a, 0, sizeof(a)) #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_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)
@ -66,6 +69,9 @@
// a pointer to the result variable. Returns true when it succeeds // a pointer to the result variable. Returns true when it succeeds
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr); uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr);
// Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode);
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
void delay_ms(uint16_t ms); void delay_ms(uint16_t ms);

View File

@ -219,6 +219,12 @@ void plan_discard_current_block()
} }
plan_block_t *plan_get_parking_block()
{
return(&block_buffer[block_buffer_head]);
}
plan_block_t *plan_get_current_block() plan_block_t *plan_get_current_block()
{ {
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
@ -251,11 +257,15 @@ uint8_t plan_check_full_buffer()
In other words, the buffer head is never equal to the buffer tail. Also the feed rate input value 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 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 is true, or as seek/rapids rate if the feed_rate value is negative (and
invert_feed_rate always false). */ invert_feed_rate always false).
The is_parking_motion boolean tells the planner to plan a motion in the always unused block buffer
head. It avoids changing the planner state and preserves the buffer to ensure subsequent gcode
motions are still planned correctly, while the stepper module only points to the block buffer head
to execute the parking motion. */
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion, int32_t line_number)
#else #else
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate) uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion)
#endif #endif
{ {
// Prepare and initialize new block // Prepare and initialize new block
@ -271,14 +281,19 @@ uint8_t plan_check_full_buffer()
// Compute and store initial move distance data. // 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 // 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. // to try to keep these types of things completely separate from the planner for portability.
int32_t target_steps[N_AXIS]; int32_t target_steps[N_AXIS], position_steps[N_AXIS];
float unit_vec[N_AXIS], delta_mm; float unit_vec[N_AXIS], delta_mm;
uint8_t idx; uint8_t idx;
// Copy position data based on type of motion being planned.
if (is_parking_motion) { memcpy(position_steps, sys.position, sizeof(sys.position)); }
else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
#ifdef COREXY #ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]); 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]); 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[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-pl.position[X_AXIS]) - (target_steps[Y_AXIS]-pl.position[Y_AXIS])); block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
#endif #endif
for (idx=0; idx<N_AXIS; idx++) { for (idx=0; idx<N_AXIS; idx++) {
@ -288,22 +303,22 @@ uint8_t plan_check_full_buffer()
#ifdef COREXY #ifdef COREXY
if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) { if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]); target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-pl.position[idx]); block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
} }
block->step_event_count = max(block->step_event_count, block->steps[idx]); block->step_event_count = max(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) { 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]; delta_mm = ((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]))/settings.steps_per_mm[idx];
} else if (idx == B_MOTOR) { } 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]; delta_mm = ((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]))/settings.steps_per_mm[idx];
} else { } else {
delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx]; delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
} }
#else #else
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]); target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-pl.position[idx]); block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
block->step_event_count = max(block->step_event_count, block->steps[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]; delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
#endif #endif
unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later. unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later.
// Set direction bits. Bit enabled always means direction is negative. // Set direction bits. Bit enabled always means direction is negative.
@ -315,7 +330,7 @@ uint8_t plan_check_full_buffer()
block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt() block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt()
// Bail if this is a zero-length block. Highly unlikely to occur. // Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return; } if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
// Adjust feed_rate value to mm/min depending on type of rate input (normal, inverse time, or rapids) // 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. // TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort.
@ -329,7 +344,7 @@ uint8_t plan_check_full_buffer()
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector. // if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
float inverse_unit_vec_value; float inverse_unit_vec_value;
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides
float junction_cos_theta = 0; float junction_cos_theta = 0.0;
for (idx=0; idx<N_AXIS; idx++) { for (idx=0; idx<N_AXIS; idx++) {
if (unit_vec[idx] != 0) { // Avoid divide by zero. if (unit_vec[idx] != 0) { // Avoid divide by zero.
unit_vec[idx] *= inverse_millimeters; // Complete unit vector calculation unit_vec[idx] *= inverse_millimeters; // Complete unit vector calculation
@ -347,9 +362,10 @@ uint8_t plan_check_full_buffer()
} }
// TODO: Need to check this method handling zero junction speeds when starting from rest. // TODO: Need to check this method handling zero junction speeds when starting from rest.
if (block_buffer_head == block_buffer_tail) { if ((block_buffer_head == block_buffer_tail) || is_parking_motion) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later. // Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
// If parking motion, the parking block always is assumed to start from rest and end at a complete stop.
block->entry_speed_sqr = 0.0; block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity. block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
@ -388,7 +404,7 @@ uint8_t plan_check_full_buffer()
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the // 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. // 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->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
(block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) ); (block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
} }
} }
@ -398,21 +414,25 @@ uint8_t plan_check_full_buffer()
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds. // 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, block->max_entry_speed_sqr = min(block->max_junction_speed_sqr,
min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr)); min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr));
// Update previous path unit_vector and nominal speed (squared) // Block parking motion from updating this data to ensure next g-code motion is computed correctly.
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[] if (!is_parking_motion) {
pl.previous_nominal_speed_sqr = block->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 // Update planner position
memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[] 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. // New block is all set. Update buffer head and next buffer head indices.
block_buffer_head = next_buffer_head; block_buffer_head = next_buffer_head;
next_buffer_head = plan_next_block_index(block_buffer_head); next_buffer_head = plan_next_block_index(block_buffer_head);
// Finish up by recalculating the plan with the new block. // Finish up by recalculating the plan with the new block.
planner_recalculate(); planner_recalculate();
}
return(PLAN_OK);
} }
@ -424,7 +444,7 @@ void plan_sync_position()
uint8_t idx; uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) { for (idx=0; idx<N_AXIS; idx++) {
#ifdef COREXY #ifdef COREXY
if (idx==A_MOTOR) { if (idx==A_MOTOR) {
pl.position[idx] = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2; pl.position[idx] = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2;
} else if (idx==B_MOTOR) { } else if (idx==B_MOTOR) {
pl.position[idx] = (sys.position[A_MOTOR] - sys.position[B_MOTOR])/2; pl.position[idx] = (sys.position[A_MOTOR] - sys.position[B_MOTOR])/2;

View File

@ -32,6 +32,9 @@
#endif #endif
#endif #endif
#define PLAN_OK true
#define PLAN_EMPTY_BLOCK false
// This struct stores a linear movement of a g-code block motion with its critical "nominal" values // This struct stores a linear movement of a g-code block motion with its critical "nominal" values
// are as specified in the source g-code. // are as specified in the source g-code.
typedef struct { typedef struct {
@ -41,7 +44,8 @@ typedef struct {
uint32_t steps[N_AXIS]; // Step count along each axis 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. 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 // Fields used by the motion planner to manage acceleration. Some of these values may be updated
// by the stepper module during execution of special motion cases for replanning purposes.
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2 float 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 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 // neighboring nominal speeds with overrides in (mm/min)^2
@ -64,15 +68,18 @@ void plan_reset();
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // 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. // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion, int32_t line_number);
#else #else
void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate); uint8_t plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_parking_motion);
#endif #endif
// Called when the current block is no longer needed. Discards the block and makes the memory // Called when the current block is no longer needed. Discards the block and makes the memory
// availible for new blocks. // availible for new blocks.
void plan_discard_current_block(); void plan_discard_current_block();
// Gets the planner block for the parking special motion case. Parking uses the always available buffer head.
plan_block_t *plan_get_parking_block();
// Gets the current block. Returns NULL if buffer empty // Gets the current block. Returns NULL if buffer empty
plan_block_t *plan_get_current_block(); plan_block_t *plan_get_current_block();

View File

@ -191,6 +191,7 @@ void printFloat_RateValue(float n) {
void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); } void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); }
void printFloat_RPMValue(float n) { printFloat(n,N_DECIMAL_RPMVALUE); }
// Debug tool to print free memory in bytes at the called point. // Debug tool to print free memory in bytes at the called point.
// NOTE: Keep commented unless using. Part of this function always gets compiled in. // NOTE: Keep commented unless using. Part of this function always gets compiled in.

View File

@ -46,11 +46,11 @@ void printFloat(float n, uint8_t decimal_places);
// - CoordValue: Handles all position or coordinate values in inches or mm reporting. // - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity 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.) // - SettingValue: Handles all floating point settings values (always in mm.)
// - RPMValue: Handles spindle RPM values in settings and reports.
void printFloat_CoordValue(float n); void printFloat_CoordValue(float n);
void printFloat_RateValue(float n); void printFloat_RateValue(float n);
void printFloat_SettingValue(float n); void printFloat_SettingValue(float n);
void printFloat_RPMValue(float n);
// Debug tool to print free memory in bytes at the called point. Not used otherwise. // Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory(); void printFreeMemory();

View File

@ -21,44 +21,15 @@
#include "grbl.h" #include "grbl.h"
// Define different comment types for pre-parsing. // Define line flags. Includes comment type tracking and line overflow detection.
#define COMMENT_NONE 0 #define LINE_FLAG_OVERFLOW bit(0)
#define COMMENT_TYPE_PARENTHESES 1 #define LINE_FLAG_COMMENT_PARENTHESES bit(1)
#define COMMENT_TYPE_SEMICOLON 2 #define LINE_FLAG_COMMENT_SEMICOLON bit(2)
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static void protocol_exec_rt_suspend();
// 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
#ifdef REPORT_ECHO_LINE_RECEIVED
report_echo_line_received(line);
#endif
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));
}
}
/* /*
@ -78,11 +49,10 @@ void protocol_main_loop()
report_feedback_message(MESSAGE_ALARM_LOCK); report_feedback_message(MESSAGE_ALARM_LOCK);
} else { } else {
// All systems go! But first check for safety door. // All systems go! But first check for safety door.
sys.state = STATE_IDLE;
if (system_check_safety_door_ajar()) { if (system_check_safety_door_ajar()) {
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state. protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
} else {
sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.
} }
system_execute_startup(line); // Execute startup script. system_execute_startup(line); // Execute startup script.
} }
@ -91,7 +61,7 @@ void protocol_main_loop()
// Primary loop! Upon a system abort, this exits back to main() to reset the system. // Primary loop! Upon a system abort, this exits back to main() to reset the system.
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
uint8_t comment = COMMENT_NONE; uint8_t line_flags = 0;
uint8_t char_counter = 0; uint8_t char_counter = 0;
uint8_t c; uint8_t c;
for (;;) { for (;;) {
@ -108,16 +78,44 @@ void protocol_main_loop()
while((c = serial_read()) != SERIAL_NO_DATA) { while((c = serial_read()) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached if ((c == '\n') || (c == '\r')) { // End of line reached
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort
line[char_counter] = 0; // Set string termination character. line[char_counter] = 0; // Set string termination character.
protocol_execute_line(line); // Line is complete. Execute it! #ifdef REPORT_ECHO_LINE_RECEIVED
comment = COMMENT_NONE; report_echo_line_received(line);
#endif
// Direct and execute one line of formatted input, and report status of execution.
if (line_flags & LINE_FLAG_OVERFLOW) {
// Report line overflow error.
report_status_message(STATUS_OVERFLOW);
} else if (line[0] == 0) {
// Empty or comment line. For syncing purposes.
report_status_message(STATUS_OK);
} else if (line[0] == '$') {
// Grbl '$' system command
report_status_message(system_execute_line(line));
} else if (sys.state == STATE_ALARM) {
// 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));
}
// Reset tracking data for next line.
line_flags = 0;
char_counter = 0; char_counter = 0;
} else { } else {
if (comment != COMMENT_NONE) {
// Throw away all comment characters if (line_flags) {
// Throw away all (except EOL) comment characters and overflow characters.
if (c == ')') { if (c == ')') {
// End of comment. Resume line. But, not if semicolon type comment. // End of '()' comment. Resume line allowed.
if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; } if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) { line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES); }
} }
} else { } else {
if (c <= ' ') { if (c <= ' ') {
@ -130,11 +128,10 @@ void protocol_main_loop()
// NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. // 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 // 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. // comment control characters, so that the g-code parser can error-check it.
comment = COMMENT_TYPE_PARENTHESES; line_flags |= LINE_FLAG_COMMENT_PARENTHESES;
} else if (c == ';') { } else if (c == ';') {
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
comment = COMMENT_TYPE_SEMICOLON; line_flags |= LINE_FLAG_COMMENT_SEMICOLON;
// TODO: Install '%' feature // TODO: Install '%' feature
// } else if (c == '%') { // } else if (c == '%') {
// Program start-end percent sign NOT SUPPORTED. // Program start-end percent sign NOT SUPPORTED.
@ -142,18 +139,16 @@ void protocol_main_loop()
// where, during a program, the system auto-cycle start will continue to execute // where, during a program, the system auto-cycle start will continue to execute
// everything until the next '%' sign. This will help fix resuming issues with certain // 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. // functions that empty the planner buffer to execute its task on-time.
} else if (char_counter >= (LINE_BUFFER_SIZE-1)) { } else if (char_counter >= (LINE_BUFFER_SIZE-1)) {
// Detect line buffer overflow. Report error and reset line buffer. // Detect line buffer overflow and set flag.
report_status_message(STATUS_OVERFLOW); line_flags |= LINE_FLAG_OVERFLOW;
comment = COMMENT_NONE;
char_counter = 0;
} else if (c >= 'a' && c <= 'z') { // Upcase lowercase } else if (c >= 'a' && c <= 'z') { // Upcase lowercase
line[char_counter++] = c-'a'+'A'; line[char_counter++] = c-'a'+'A';
} else { } else {
line[char_counter++] = c; line[char_counter++] = c;
} }
} }
} }
} }
@ -171,209 +166,6 @@ void protocol_main_loop()
} }
// 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.
do { // If system is suspended, suspend loop restarts here.
// 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);
} else if (rt_exec & EXEC_ALARM_HOMING_FAIL) {
report_alarm_message(ALARM_HOMING_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.
// TODO: Allow status reports during a critical alarm. Still need to think about implications of this.
// if (sys.rt_exec_state & EXEC_STATUS_REPORT) {
// report_realtime_status();
// bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
// }
} 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 hold states.
// NOTE: The math involved to calculate the hold should be low enough for most, if not all,
// operational scenarios. Once hold is initiated, the system enters a suspend state to block
// all main program processes until either reset or resumed.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {
// If in CYCLE state, all hold states immediately initiate a motion HOLD.
if (sys.state == STATE_CYCLE) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.suspend = SUSPEND_ENABLE_HOLD; // Initiate holding cycle with flag.
}
// If IDLE, Grbl is not in motion. Simply indicate suspend ready state.
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_ENABLE_READY; }
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. If so, only flag that motion cancel is complete.
if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }
sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming. Special motion complete.
}
// Execute a feed hold with deceleration, only during cycle.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD.
if (bit_isfalse(sys.state,STATE_SAFETY_DOOR)) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold, only during a cycle, and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged. The power-down is
// executed here, if IDLE, or when the CYCLE completes via the EXEC_CYCLE_STOP flag.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// If already in active, ready-to-resume HOLD, set CYCLE_STOP flag to force de-energize.
// NOTE: Only temporarily sets the 'rt_exec' variable, not the volatile 'rt_exec_state' variable.
if (sys.suspend & SUSPEND_ENABLE_READY) { bit_true(rt_exec,EXEC_CYCLE_STOP); }
sys.suspend |= SUSPEND_ENERGIZE;
sys.state = STATE_SAFETY_DOOR;
}
}
bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
if (rt_exec & EXEC_CYCLE_START) {
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// Cycle start only when IDLE or when a hold is complete and ready to resume.
// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_ENABLE_READY))) {
// Re-energize powered components, if disabled by SAFETY_DOOR.
if (sys.suspend & SUSPEND_ENERGIZE) {
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed);
delay_ms(SAFETY_DOOR_SPINDLE_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
coolant_set_state(gc_state.modal.coolant);
delay_ms(SAFETY_DOOR_COOLANT_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
// TODO: Install return to pre-park position.
}
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
sys.state = STATE_CYCLE;
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
st_wake_up();
} else { // Otherwise, do nothing. Set and resume IDLE state.
sys.state = STATE_IDLE;
}
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
}
}
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 (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
if (sys.suspend & SUSPEND_ENERGIZE) { // De-energize system if safety door has been opened.
spindle_stop();
coolant_stop();
// TODO: Install parking motion here.
}
bit_true(sys.suspend,SUSPEND_ENABLE_READY);
} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states.
sys.suspend = SUSPEND_DISABLE;
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_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) { st_prep_buffer(); }
// If safety door was opened, actively check when safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (bit_istrue(sys.suspend,SUSPEND_ENABLE_READY)) {
if (!(system_check_safety_door_ajar())) {
sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume.
}
}
}
} while(sys.suspend); // Check for system suspend state before exiting.
}
// Block until all buffered steps are executed or in a cycle state. Works with feed hold // 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. // during a synchronize call, if it should happen. Also, waits for clean cycle end.
void protocol_buffer_synchronize() void protocol_buffer_synchronize()
@ -399,3 +191,366 @@ void protocol_buffer_synchronize()
// is finished, single commands), a command that needs to wait for the motions in the buffer to // is finished, single commands), a command that needs to wait for the motions in the buffer to
// execute calls a buffer sync, or the planner buffer is full and ready to go. // execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start() { bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); } void protocol_auto_cycle_start() { bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); }
// This function is the general interface to Grbl's real-time command execution system. It is called
// from various check points in the main program, primarily where there may be a while loop waiting
// for a buffer to clear space or any point where the execution time from the last check point may
// be more than a fraction of a second. This is a way to execute realtime commands asynchronously
// (aka multitasking) with grbl's g-code parsing and planning functions. This function also serves
// as an interface for the interrupts to set the system realtime flags, where only the main program
// handles them, removing the need to define more computationally-expensive volatile variables. This
// also provides a controlled way to execute certain tasks without having two or more instances of
// the same task, such as the planner recalculating the buffer upon a feedhold or overrides.
// NOTE: The sys.rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts,
// limit switches, or the main program.
void protocol_execute_realtime()
{
protocol_exec_rt_system();
if (sys.suspend) { protocol_exec_rt_suspend(); }
}
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// NOTE: Do not alter this unless you know exactly what you are doing!
void protocol_exec_rt_system()
{
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
rt_exec = sys.rt_exec_alarm; // Copy volatile sys.rt_exec_alarm.
if (rt_exec) { // Enter only if any bit flag is true
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
// the source of the error to the user. If critical, Grbl disables by entering an infinite
// loop until system reset/abort.
sys.state = STATE_ALARM; // Set system alarm state
if (rt_exec & EXEC_ALARM_HARD_LIMIT) {
report_alarm_message(ALARM_HARD_LIMIT_ERROR);
} else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) {
report_alarm_message(ALARM_SOFT_LIMIT_ERROR);
} else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) {
report_alarm_message(ALARM_ABORT_CYCLE);
} else if (rt_exec & EXEC_ALARM_PROBE_FAIL) {
report_alarm_message(ALARM_PROBE_FAIL);
} else if (rt_exec & EXEC_ALARM_HOMING_FAIL) {
report_alarm_message(ALARM_HOMING_FAIL);
}
// 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 {
// Block everything, except reset and status reports, until user issues reset or power
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
// the user and a GUI time to do what is needed before resetting, like killing the
// incoming stream. The same could be said about soft limits. While the position is not
// lost, streaming could cause a serious crash if it continues afterwards.
// TODO: Allow status reports during a critical alarm. Still need to think about implications of this.
// if (sys.rt_exec_state & EXEC_STATUS_REPORT) {
// report_realtime_status();
// bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
// }
} while (bit_isfalse(sys.rt_exec_state,EXEC_RESET));
}
bit_false_atomic(sys.rt_exec_alarm,0xFF); // Clear all alarm flags
}
rt_exec = sys.rt_exec_state; // Copy volatile sys.rt_exec_state.
if (rt_exec) {
// Execute system abort.
if (rt_exec & EXEC_RESET) {
sys.abort = true; // Only place this is set true.
return; // Nothing else to do but exit.
}
// Execute and serial print status
if (rt_exec & EXEC_STATUS_REPORT) {
report_realtime_status();
bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
}
// NOTE: The math involved to calculate the hold should be low enough for most, if not all,
// operational scenarios. Once hold is initiated, the system enters a suspend state to block
// all main program processes until either reset or resumed.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {
// If in CYCLE state, all hold states immediately initiate a motion HOLD.
if (sys.state == STATE_CYCLE) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
}
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
if (sys.state == STATE_IDLE) {
sys.suspend = SUSPEND_HOLD_COMPLETE;
sys.step_control = STEP_CONTROL_END_MOTION;
}
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. If so, only flag that motion cancel is complete.
if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }
// NOTE: Ensures the motion cancel is handled correctly if it is active during a HOLD or DOOR state.
sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming.
}
// Execute a feed hold with deceleration, if required. Then, suspend system.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD, which should only
// occur if the safety door switch closes.
if (sys.state != STATE_SAFETY_DOOR) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// Check if the safety re-opened during a restore parking motion only. Ignore if
// already retracting or parked.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
if (sys.suspend & SUSPEND_INITIATE_RESTORE) { // Actively restoring
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK);
sys.suspend &= ~(SUSPEND_HOLD_COMPLETE);
} // else NO_MOTION is active.
#endif
sys.suspend &= ~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend |= SUSPEND_RESTART_RETRACT;
}
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
// are executed if the door switch closes and the state returns to HOLD.
sys.suspend |= SUSPEND_SAFETY_DOOR_AJAR;
sys.state = STATE_SAFETY_DOOR;
}
}
bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
if (rt_exec & EXEC_CYCLE_START) {
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// Cycle start only when IDLE or when a hold is complete and ready to resume.
// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
if bit_isfalse(sys.suspend,SUSPEND_RESTORE_COMPLETE) {
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// the retraction execution is complete, which implies the initial feed hold is not active. To
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
sys.suspend |= SUSPEND_INITIATE_RESTORE;
} else {
bit_false(sys.suspend,SUSPEND_SAFETY_DOOR_AJAR);
}
}
}
if (!(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
sys.state = STATE_CYCLE;
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
st_wake_up();
} else { // Otherwise, do nothing. Set and resume IDLE state.
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
sys.state = STATE_IDLE;
}
}
}
}
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_START);
}
if (rt_exec & EXEC_CYCLE_STOP) {
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// realtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
plan_cycle_reinitialize();
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { sys.suspend |= SUSPEND_HOLD_COMPLETE; }
bit_false(sys.step_control,(STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_PARK));
} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states.
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
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_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) {
st_prep_buffer();
}
}
// Handles Grbl system suspend procedures, such as feed hold, safety door, and parking motion.
// The system will enter this loop, create local variables for suspend tasks, and return to
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
// This function is written in a way to promote custom parking motions. Simply use this as a
// template
static void protocol_exec_rt_suspend()
{
#ifdef PARKING_ENABLE
// Declare parking local variables
float restore_target[N_AXIS];
float parking_target[N_AXIS];
float retract_waypoint = PARKING_PULLOUT_INCREMENT;
#endif
while (sys.suspend) {
if (sys.abort) { return; }
// Safety door manager. Handles de/re-energizing, switch state checks, and parking motions.
if ((sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) && (sys.suspend & SUSPEND_HOLD_COMPLETE)) {
// Handles retraction motions and de-energizing.
if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
#ifndef PARKING_ENABLE
spindle_stop(); // De-energize
coolant_stop(); // De-energize
#else
// Get current position and store restore location and spindle retract waypoint.
system_convert_array_steps_to_mpos(parking_target,sys.position);
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
memcpy(restore_target,parking_target,sizeof(parking_target));
retract_waypoint += restore_target[PARKING_AXIS];
retract_waypoint = min(retract_waypoint,PARKING_TARGET);
}
// Execute slow pull-out parking retract motion. Parking requires homing enabled and
// the current location not exceeding the parking target location.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET)) {
// Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location.
if (parking_target[PARKING_AXIS] < retract_waypoint) {
parking_target[PARKING_AXIS] = retract_waypoint;
mc_parking_motion(parking_target, PARKING_PULLOUT_RATE);
}
spindle_stop(); // De-energize
coolant_stop(); // De-energize
// Execute fast parking retract motion to parking target location.
if (parking_target[PARKING_AXIS] < PARKING_TARGET) {
parking_target[PARKING_AXIS] = PARKING_TARGET;
mc_parking_motion(parking_target, PARKING_RATE);
}
} else {
// Parking motion not possible. Just disable the spindle and coolant.
spindle_stop(); // De-energize
coolant_stop(); // De-energize
}
#endif
sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
sys.suspend |= SUSPEND_RETRACT_COMPLETE;
} else {
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume.
}
}
// Handles parking restore and safety door resume.
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
#ifdef PARKING_ENABLE
// Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
parking_target[PARKING_AXIS] = retract_waypoint;
mc_parking_motion(parking_target, PARKING_RATE);
}
}
#endif
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SAFETY_DOOR);
}
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
coolant_set_state(gc_state.modal.coolant);
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SAFETY_DOOR);
}
}
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// Regardless if the retract parking motion was a valid/safe motion or not, the
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
mc_parking_motion(restore_target, PARKING_PULLOUT_RATE);
}
}
#endif
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
bit_true_atomic(sys.rt_exec_state,EXEC_CYCLE_START); // Set to resume program.
}
}
}
}
protocol_exec_rt_system();
}
}

View File

@ -38,6 +38,7 @@ void protocol_main_loop();
// Checks and executes a realtime command at various stop points in main program // Checks and executes a realtime command at various stop points in main program
void protocol_execute_realtime(); void protocol_execute_realtime();
void protocol_exec_rt_system();
// Notify the stepper subsystem to start executing the g-code program in buffer. // Notify the stepper subsystem to start executing the g-code program in buffer.
// void protocol_cycle_start(); // void protocol_cycle_start();

View File

@ -73,6 +73,8 @@ void report_status_message(uint8_t status_code)
case STATUS_MAX_STEP_RATE_EXCEEDED: case STATUS_MAX_STEP_RATE_EXCEEDED:
printPgmString(PSTR("Step rate > 30kHz")); break; printPgmString(PSTR("Step rate > 30kHz")); break;
#endif #endif
case STATUS_CHECK_DOOR:
printPgmString(PSTR("Check Door")); break;
// Common g-code parser errors. // Common g-code parser errors.
case STATUS_GCODE_MODAL_GROUP_VIOLATION: case STATUS_GCODE_MODAL_GROUP_VIOLATION:
printPgmString(PSTR("Modal group violation")); break; printPgmString(PSTR("Modal group violation")); break;
@ -196,6 +198,8 @@ void report_grbl_settings() {
printPgmString(PSTR("\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate); printPgmString(PSTR("\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate);
printPgmString(PSTR("\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay); printPgmString(PSTR("\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay);
printPgmString(PSTR("\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff); printPgmString(PSTR("\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR("\r\n$30=")); printFloat_RPMValue(settings.rpm_max);
printPgmString(PSTR("\r\n$31=")); printFloat_RPMValue(settings.rpm_min);
printPgmString(PSTR("\r\n")); printPgmString(PSTR("\r\n"));
#else #else
printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds); printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds);
@ -221,7 +225,9 @@ void report_grbl_settings() {
printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_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 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 debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);
printPgmString(PSTR(" (homing pull-off, mm)\r\n")); printPgmString(PSTR(" (homing pull-off, mm)\r\n$30=")); printFloat_RPMValue(settings.rpm_max);
printPgmString(PSTR(" (rpm max)\r\n$31=")); printFloat_RPMValue(settings.rpm_min);
printPgmString(PSTR(" (rpm min)\r\n"));
#endif #endif
// Print axis settings // Print axis settings
@ -380,7 +386,7 @@ void report_gcode_modes()
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
printPgmString(PSTR(" S")); printPgmString(PSTR(" S"));
printFloat_RateValue(gc_state.spindle_speed); printFloat_RPMValue(gc_state.spindle_speed);
#endif #endif
printPgmString(PSTR("]\r\n")); printPgmString(PSTR("]\r\n"));

View File

@ -34,6 +34,7 @@
#define STATUS_SOFT_LIMIT_ERROR 10 #define STATUS_SOFT_LIMIT_ERROR 10
#define STATUS_OVERFLOW 11 #define STATUS_OVERFLOW 11
#define STATUS_MAX_STEP_RATE_EXCEEDED 12 #define STATUS_MAX_STEP_RATE_EXCEEDED 12
#define STATUS_CHECK_DOOR 13
#define STATUS_GCODE_UNSUPPORTED_COMMAND 20 #define STATUS_GCODE_UNSUPPORTED_COMMAND 20
#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21 #define STATUS_GCODE_MODAL_GROUP_VIOLATION 21

View File

@ -65,6 +65,10 @@ void settings_restore(uint8_t restore_flag) {
settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK; settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.rpm_max = DEFAULT_SPINDLE_RPM_MAX;
settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN;
settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE; settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE; settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
@ -265,6 +269,8 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 25: settings.homing_seek_rate = value; break; case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break; case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break; case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = value; break;
case 31: settings.rpm_min = value; break;
default: default:
return(STATUS_INVALID_STATEMENT); return(STATUS_INVALID_STATEMENT);
} }

View File

@ -27,7 +27,7 @@
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // 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 // when firmware is upgraded. Always stored in byte 0 of eeprom
#define SETTINGS_VERSION 9 // NOTE: Check settings_reset() when moving to next version. #define SETTINGS_VERSION 10 // NOTE: Check settings_reset() when moving to next version.
// Define bit flag masks for the boolean settings in settings.flag. // Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0) #define BITFLAG_REPORT_INCHES bit(0)
@ -92,6 +92,9 @@ typedef struct {
float junction_deviation; float junction_deviation;
float arc_tolerance; float arc_tolerance;
float rpm_max;
float rpm_min;
uint8_t flags; // Contains default boolean settings uint8_t flags; // Contains default boolean settings
uint8_t homing_dir_mask; uint8_t homing_dir_mask;

View File

@ -24,21 +24,33 @@
void spindle_init() void spindle_init()
{ {
// Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
// combined unless configured otherwise.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
// combined unless configured otherwise.
SPINDLE_PWM_DDR |= (1<<SPINDLE_PWM_BIT); // Configure as PWM output pin. SPINDLE_PWM_DDR |= (1<<SPINDLE_PWM_BIT); // Configure as PWM output pin.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) TCCRA_REGISTER = TCCRA_INIT_MASK; // Configure PWM output compare timer
TCCRB_REGISTER = TCCRB_INIT_MASK;
#ifdef CPU_MAP_ATMEGA2560
OCRA_REGISTER = OCRA_TOP_VALUE; // Set the top value for 16-bit fast PWM mode
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin. SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#else // Otherwise 328p
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
#else
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif
#endif #endif
// Configure no variable spindle and only enable pin.
#else #else
// Configure no variable spindle and only enable pin.
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin. SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif #endif
#ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif
spindle_stop(); spindle_stop();
} }
@ -50,23 +62,25 @@ void spindle_stop()
TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero. TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) #if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef INVERT_SPINDLE_ENABLE_PIN #ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else #else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif #endif
#endif #endif
#else #else
#ifdef INVERT_SPINDLE_ENABLE_PIN #ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else #else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif #endif
#endif #endif
} }
void spindle_set_state(uint8_t state, float rpm) void spindle_set_state(uint8_t state, float rpm)
{ {
if (sys.abort) { return; } // Block during abort.
// Halt or set spindle direction and rpm. // Halt or set spindle direction and rpm.
if (state == SPINDLE_DISABLE) { if (state == SPINDLE_DISABLE) {
@ -83,29 +97,32 @@ void spindle_set_state(uint8_t state, float rpm)
#endif #endif
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// TODO: Install the optional capability for frequency-based output for servos. // TODO: Install the optional capability for frequency-based output for servos.
#ifdef CPU_MAP_ATMEGA2560 #ifdef CPU_MAP_ATMEGA2560
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER); uint16_t current_pwm; // 2560 PWM register is 16-bit.
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 #else
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER); uint8_t current_pwm; // 328p PWM register is 8-bit.
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler
uint8_t current_pwm;
#endif #endif
#define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM) // Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ( rpm < SPINDLE_MIN_RPM ) { rpm = 0; } if (settings.rpm_max <= settings.rpm_min) {
else { // No PWM range possible. Set simple on/off spindle control pin state.
rpm -= SPINDLE_MIN_RPM; current_pwm = PWM_MAX_VALUE;
if ( rpm > SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent integer overflow } else {
if (rpm > settings.rpm_max) { rpm = settings.rpm_max; }
if (rpm < settings.rpm_min) { rpm = settings.rpm_min; }
#ifdef SPINDLE_MINIMUM_PWM
float pwm_gradient = (PWM_MAX_VALUE-SPINDLE_MINIMUM_PWM)/(settings.rpm_max-settings.rpm_min);
current_pwm = floor( (rpm-settings.rpm_min)*pwm_gradient + (SPINDLE_MINIMUM_PWM+0.5));
#else
float pwm_gradient = (PWM_MAX_VALUE)/(settings.rpm_max-settings.rpm_min);
current_pwm = floor( (rpm-settings.rpm_min)*pwm_gradient + 0.5);
#endif
} }
current_pwm = floor( rpm*(PWM_MAX_VALUE/SPINDLE_RPM_RANGE) + 0.5);
#ifdef MINIMUM_SPINDLE_PWM OCR_REGISTER = current_pwm; // Set PWM output level.
if (current_pwm < MINIMUM_SPINDLE_PWM) { current_pwm = MINIMUM_SPINDLE_PWM; } TCCRA_REGISTER |= (1<<COMB_BIT); // Ensure PWM output is enabled.
#endif
OCR_REGISTER = current_pwm; // Set PWM pin output
// On the Uno, spindle enable and PWM are shared, unless otherwise specified. // On the Uno, spindle enable and PWM are shared, unless otherwise specified.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) #if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
@ -117,11 +134,13 @@ void spindle_set_state(uint8_t state, float rpm)
#endif #endif
#else #else
#ifdef INVERT_SPINDLE_ENABLE_PIN #ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else #else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif #endif
#endif #endif
} }

View File

@ -29,6 +29,10 @@
#define RAMP_CRUISE 1 #define RAMP_CRUISE 1
#define RAMP_DECEL 2 #define RAMP_DECEL 2
#define PREP_FLAG_RECALCULATE bit(0)
#define PREP_FLAG_HOLD_PARTIAL_BLOCK bit(1)
#define PREP_FLAG_PARKING bit(2)
// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level // 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 // 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 // starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
@ -120,12 +124,20 @@ static st_block_t *st_prep_block; // Pointer to the stepper block data being pr
// based on the current executing planner block. // based on the current executing planner block.
typedef struct { typedef struct {
uint8_t st_block_index; // Index of stepper common data block being prepped 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. uint8_t recalculate_flag;
float steps_remaining;
float step_per_mm; // Current planner block step/millimeter conversion scalar
float req_mm_increment;
float dt_remainder; float dt_remainder;
float steps_remaining;
float step_per_mm;
float req_mm_increment;
#ifdef PARKING_ENABLE
uint8_t last_st_block_index;
float last_steps_remaining;
float last_step_per_mm;
float last_req_mm_increment;
float last_dt_remainder;
#endif
uint8_t ramp_type; // Current segment ramp state uint8_t ramp_type; // Current segment ramp state
float mm_complete; // End of velocity profile from end of current planner block in (mm). float mm_complete; // End of velocity profile from end of current planner block in (mm).
@ -186,7 +198,7 @@ void st_wake_up()
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); } if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); } else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
if (sys.state & (STATE_CYCLE | STATE_HOMING)){ // if (sys.state & (STATE_CYCLE | STATE_HOMING)){
// Initialize stepper output bits // Initialize stepper output bits
st.dir_outbits = dir_port_invert_mask; st.dir_outbits = dir_port_invert_mask;
st.step_outbits = step_port_invert_mask; st.step_outbits = step_port_invert_mask;
@ -204,7 +216,7 @@ void st_wake_up()
// Enable Stepper Driver Interrupt // Enable Stepper Driver Interrupt
TIMSK1 |= (1<<OCIE1A); TIMSK1 |= (1<<OCIE1A);
} // }
} }
@ -499,13 +511,60 @@ void stepper_init()
void st_update_plan_block_parameters() void st_update_plan_block_parameters()
{ {
if (pl_block != NULL) { // Ignore if at start of a new block. if (pl_block != NULL) { // Ignore if at start of a new block.
prep.flag_partial_block = true; prep.recalculate_flag |= PREP_FLAG_RECALCULATE;
pl_block->entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed. 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. pl_block = NULL; // Flag st_prep_segment() to load and check active velocity profile.
} }
} }
// Increments the step segment buffer block data ring buffer.
static uint8_t st_next_block_index(uint8_t block_index)
{
block_index++;
if ( block_index == (SEGMENT_BUFFER_SIZE-1) ) { return(0); }
return(block_index);
}
#ifdef PARKING_ENABLE
// Changes the run state of the step segment buffer to execute the special parking motion.
void st_parking_setup_buffer()
{
// Store step execution data of partially completed block, if necessary.
if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
prep.last_st_block_index = prep.st_block_index;
prep.last_steps_remaining = prep.steps_remaining;
prep.last_dt_remainder = prep.dt_remainder;
prep.last_step_per_mm = prep.step_per_mm;
}
// Set flags to execute a parking motion
prep.recalculate_flag |= PREP_FLAG_PARKING;
prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE);
pl_block = NULL; // Always reset parking motion to reload new block.
}
// Restores the step segment buffer to the normal run state after a parking motion.
// NOTE: This function does not compile if parking is disabled.
void st_parking_restore_buffer()
{
// Restore step execution data and flags of partially completed block, if necessary.
if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
prep.st_block_index = prep.last_st_block_index;
prep.steps_remaining = prep.last_steps_remaining;
prep.dt_remainder = prep.last_dt_remainder;
prep.step_per_mm = prep.last_step_per_mm;
st_prep_block = &st_block_buffer[prep.st_block_index];
prep.recalculate_flag = (PREP_FLAG_HOLD_PARTIAL_BLOCK | PREP_FLAG_RECALCULATE);
} else {
prep.recalculate_flag = false;
}
pl_block = NULL; // Set to reload next block.
}
#endif
/* Prepares step segment buffer. Continuously called from main program. /* Prepares step segment buffer. Continuously called from main program.
The segment buffer is an intermediary buffer interface between the execution of steps The segment buffer is an intermediary buffer interface between the execution of steps
@ -521,120 +580,140 @@ void st_update_plan_block_parameters()
*/ */
void st_prep_buffer() void st_prep_buffer()
{ {
// Block step prep buffer, while in a suspend state and there is no suspend motion to execute.
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { if (bit_istrue(sys.step_control,STEP_CONTROL_END_MOTION)) { return; }
// Check if we still need to generate more segments for a motion suspend.
if (prep.current_speed == 0.0) { return; } // Nothing to do. Bail.
}
while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the 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. // Determine if we need to load a new planner block or if the block needs to be recomputed.
if (pl_block == NULL) { 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 #ifdef PARKING_ENABLE
// data for the block. If not, we are still mid-block and the velocity profile was updated.
if (prep.flag_partial_block) { // Query planner for a queued block
prep.flag_partial_block = false; // Reset flag if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) { pl_block = plan_get_parking_block(); }
else { pl_block = plan_get_current_block(); }
if (pl_block == NULL) { return; } // No planner blocks. Exit.
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
if (prep.recalculate_flag & PREP_FLAG_PARKING) { prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE); }
else { prep.recalculate_flag = false; }
#else
// Query planner for a queued block
pl_block = plan_get_current_block();
if (pl_block == NULL) { return; } // No planner blocks. Exit.
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
prep.recalculate_flag = false;
#endif
} else { } 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; } // Load the Bresenham stepping data for the block.
prep.st_block_index = st_next_block_index(prep.st_block_index);
// Prepare and copy Bresenham algorithm segment data from the new planner block, so that // 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 // 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. // 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 = &st_block_buffer[prep.st_block_index];
st_prep_block->direction_bits = pl_block->direction_bits; st_prep_block->direction_bits = pl_block->direction_bits;
uint8_t idx;
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING #ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS]; for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = pl_block->steps[idx]; }
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; st_prep_block->step_event_count = pl_block->step_event_count;
#else #else
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS // With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
// level, such that we never divide beyond the original data anywhere in the algorithm. // 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. // 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; for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = pl_block->steps[idx] << 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; st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
#endif #endif
// Initialize segment buffer data for generating the segments. // Initialize segment buffer data for generating the segments.
prep.steps_remaining = pl_block->step_event_count; prep.steps_remaining = (float)pl_block->step_event_count;
prep.step_per_mm = prep.steps_remaining/pl_block->millimeters; prep.step_per_mm = prep.steps_remaining/pl_block->millimeters;
prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm; prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm;
prep.dt_remainder = 0.0; // Reset for new segment block
prep.dt_remainder = 0.0; // Reset for new planner block if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) {
// New block loaded mid-hold. Override planner block entry speed to enforce deceleration.
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Override planner block entry speed and enforce deceleration during feed hold.
prep.current_speed = prep.exit_speed; prep.current_speed = prep.exit_speed;
pl_block->entry_speed_sqr = prep.exit_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);
} }
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 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 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 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. 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. 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; float inv_2_accel = 0.5/pl_block->acceleration;
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // [Forced Deceleration to Zero Velocity] if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { // [Forced Deceleration to Zero Velocity]
// Compute velocity profile parameters for a feed hold in-progress. This profile overrides // Compute velocity profile parameters for a feed hold in-progress. This profile overrides
// the planner block profile, enforcing a deceleration to zero speed. // the planner block profile, enforcing a deceleration to zero speed.
prep.ramp_type = RAMP_DECEL; prep.ramp_type = RAMP_DECEL;
// Compute decelerate distance relative to end of block. // Compute decelerate distance relative to end of block.
float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr; float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr;
if (decel_dist < 0.0) { if (decel_dist < 0.0) {
// Deceleration through entire planner block. End of feed hold is not in this block. // 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); prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
} else { } else {
prep.mm_complete = decel_dist; // End of feed hold. prep.mm_complete = decel_dist; // End of feed hold.
prep.exit_speed = 0.0; prep.exit_speed = 0.0;
} }
} else { // [Normal Operation] } else { // [Normal Operation]
// Compute or recompute velocity profile parameters of the prepped planner block. // Compute or recompute velocity profile parameters of the prepped planner block.
prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp. prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp.
prep.accelerate_until = pl_block->millimeters; 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; #ifdef PARKING_ENABLE
float intersect_distance = if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) { prep.exit_speed = 0.0; }
0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr)); else { prep.exit_speed = plan_get_exec_block_exit_speed(); }
if (intersect_distance > 0.0) { #else
if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types prep.exit_speed = plan_get_exec_block_exit_speed();
// NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0. #endif
prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr);
if (prep.decelerate_after < intersect_distance) { // Trapezoid type float exit_speed_sqr = prep.exit_speed*prep.exit_speed;
prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr); float intersect_distance =
if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) { 0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr));
// Cruise-deceleration or cruise-only type. if (intersect_distance > 0.0) {
prep.ramp_type = RAMP_CRUISE; if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types
} else { // NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0.
// Full-trapezoid or acceleration-cruise types prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr);
prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr); if (prep.decelerate_after < intersect_distance) { // Trapezoid type
} prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr);
} else { // Triangle type if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) {
prep.accelerate_until = intersect_distance; // Cruise-deceleration or cruise-only type.
prep.decelerate_after = intersect_distance; prep.ramp_type = RAMP_CRUISE;
prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr); } else {
} // Full-trapezoid or acceleration-cruise types
} else { // Deceleration-only type prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr);
prep.ramp_type = RAMP_DECEL; }
// prep.decelerate_after = pl_block->millimeters; } else { // Triangle type
prep.maximum_speed = prep.current_speed; prep.accelerate_until = intersect_distance;
} prep.decelerate_after = intersect_distance;
} else { // Acceleration-only type prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr);
prep.accelerate_until = 0.0; }
// prep.decelerate_after = 0.0; } else { // Deceleration-only type
prep.maximum_speed = prep.exit_speed; prep.ramp_type = RAMP_DECEL;
} // prep.decelerate_after = pl_block->millimeters;
} // prep.maximum_speed = prep.current_speed;
}
} else { // Acceleration-only type
prep.accelerate_until = 0.0;
// prep.decelerate_after = 0.0;
prep.maximum_speed = prep.exit_speed;
}
}
} }
// Initialize new segment // Initialize new segment
@ -703,14 +782,16 @@ void st_prep_buffer()
if (prep.current_speed > speed_var) { // Check if at or below zero speed. if (prep.current_speed > speed_var) { // Check if at or below zero speed.
// Compute distance from end of segment to end of block. // Compute distance from end of segment to end of block.
mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm) mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm)
if (mm_var > prep.mm_complete) { // Deceleration only. if (mm_var > prep.mm_complete) { // Typical case. In deceleration ramp.
mm_remaining = mm_var; mm_remaining = mm_var;
prep.current_speed -= speed_var; prep.current_speed -= speed_var;
break; // Segment complete. Exit switch-case statement. Continue do-while loop. break; // Segment complete. Exit switch-case statement. Continue do-while loop.
} }
} // End of block or end of forced-deceleration. }
// Otherwise, at end of block or end of forced-deceleration.
time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed); time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed);
mm_remaining = prep.mm_complete; mm_remaining = prep.mm_complete;
prep.current_speed = prep.exit_speed;
} }
dt += time_var; // Add computed ramp time to total segment time. dt += time_var; // Add computed ramp time to total segment time.
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction. if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
@ -737,21 +818,20 @@ void st_prep_buffer()
Fortunately, this scenario is highly unlikely and unrealistic in CNC machines 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). 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 step_dist_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 n_steps_remaining = ceil(step_dist_remaining); // Round-up current steps remaining
float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining 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. 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. // 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 (prep_segment->n_step == 0) {
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) {
// Less than one step to decelerate to zero speed, but already very close. AMASS // Less than one step to decelerate to zero speed, but already very close. AMASS
// requires full steps to execute. So, just bail. // requires full steps to execute. So, just bail.
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
prep.dt_remainder = 0.0; #ifdef PARKING_ENABLE
prep.steps_remaining = n_steps_remaining; if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. #endif
plan_cycle_reinitialize();
return; // Segment not generated, but current step data still retained. return; // Segment not generated, but current step data still retained.
} }
} }
@ -765,8 +845,7 @@ void st_prep_buffer()
// typically very small and do not adversely effect performance, but ensures that Grbl // 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. // outputs the exact acceleration and velocity profiles as computed by the planner.
dt += prep.dt_remainder; // Apply previous segment partial step execute time 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 float inv_rate = dt/(last_n_steps_remaining - step_dist_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. // Compute CPU cycles per step for the prepped segment.
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step) uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
@ -802,29 +881,35 @@ void st_prep_buffer()
} }
#endif #endif
// Segment complete! Increment segment buffer indices. // Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it.
segment_buffer_head = segment_next_head; segment_buffer_head = segment_next_head;
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; } if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
// Setup initial conditions for next segment. // Update the appropriate planner and segment data.
if (mm_remaining > prep.mm_complete) { pl_block->millimeters = mm_remaining;
// Normal operation. Block incomplete. Distance remaining in block to be executed. prep.steps_remaining = n_steps_remaining;
pl_block->millimeters = mm_remaining; prep.dt_remainder = (n_steps_remaining - step_dist_remaining)*inv_rate;
prep.steps_remaining = steps_remaining;
} else { // Check for exit conditions and flag to load next planner block.
if (mm_remaining == prep.mm_complete) {
// End of planner block or forced-termination. No more distance to be executed. // End of planner block or forced-termination. No more distance to be executed.
if (mm_remaining > 0.0) { // At end of forced-termination. if (mm_remaining > 0.0) { // At end of forced-termination.
// Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete // Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete
// the segment queue, where realtime protocol will set new state upon receiving the // the segment queue, where realtime protocol will set new state upon receiving the
// cycle stop flag from the ISR. Prep_segment is blocked until then. // cycle stop flag from the ISR. Prep_segment is blocked until then.
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
prep.dt_remainder = 0.0; #ifdef PARKING_ENABLE
prep.steps_remaining = ceil(steps_remaining); if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. #endif
plan_cycle_reinitialize();
return; // Bail! return; // Bail!
} else { // End of planner block } else { // End of planner block
// The planner block is complete. All steps are set to be executed in the segment buffer. // The planner block is complete. All steps are set to be executed in the segment buffer.
#ifdef PARKING_ENABLE
if (sys.step_control & STEP_CONTROL_EXECUTE_PARK) {
bit_true(sys.step_control,STEP_CONTROL_END_MOTION);
return;
}
#endif
pl_block = NULL; // Set pointer to indicate check and load next planner block. pl_block = NULL; // Set pointer to indicate check and load next planner block.
plan_discard_current_block(); plan_discard_current_block();
} }

View File

@ -41,6 +41,12 @@ void st_generate_step_dir_invert_masks();
// Reset the stepper subsystem variables // Reset the stepper subsystem variables
void st_reset(); void st_reset();
// Changes the run state of the step segment buffer to execute the special parking motion.
void st_parking_setup_buffer();
// Restores the step segment buffer to the normal run state after a parking motion.
void st_parking_restore_buffer();
// Reloads step segment buffer. Called continuously by realtime execution system. // Reloads step segment buffer. Called continuously by realtime execution system.
void st_prep_buffer(); void st_prep_buffer();

View File

@ -135,13 +135,11 @@ uint8_t system_execute_line(char *line)
break; break;
case 'X' : // Disable alarm lock [ALARM] case 'X' : // Disable alarm lock [ALARM]
if (sys.state == STATE_ALARM) { if (sys.state == STATE_ALARM) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); }
report_feedback_message(MESSAGE_ALARM_UNLOCK); report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE; sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents. // Don't run startup script. Prevents stored moves in startup from causing accidents.
if (system_check_safety_door_ajar()) { // Check safety door switch before returning.
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode.
}
} // Otherwise, no effect. } // Otherwise, no effect.
break; break;
// case 'J' : break; // Jogging methods // case 'J' : break; // Jogging methods
@ -156,8 +154,6 @@ uint8_t system_execute_line(char *line)
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the // 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 // 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. // on its own carefully. This approach could be effective and possibly size/memory efficient.
// }
// break;
} }
break; break;
default : default :
@ -170,16 +166,9 @@ uint8_t system_execute_line(char *line)
break; break;
case 'H' : // Perform homing cycle [IDLE/ALARM] case 'H' : // Perform homing cycle [IDLE/ALARM]
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); }
sys.state = STATE_HOMING; // Set system state variable sys.state = STATE_HOMING; // Set system state variable
// Only perform homing if Grbl is idle or lost.
// TODO: Likely not required.
if (system_check_safety_door_ajar()) { // Check safety door switch before homing.
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode.
}
mc_homing_cycle(); mc_homing_cycle();
if (!sys.abort) { // Execute startup scripts after successful homing. if (!sys.abort) { // Execute startup scripts after successful homing.
sys.state = STATE_IDLE; // Set to IDLE when complete. sys.state = STATE_IDLE; // Set to IDLE when complete.

View File

@ -59,12 +59,23 @@
#define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system. #define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system.
#define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle. #define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle.
// Define system suspend states. // Define system suspend flags. Used in various ways to manage suspend states and procedures.
#define SUSPEND_DISABLE 0 // Must be zero. #define SUSPEND_DISABLE 0 // Must be zero.
#define SUSPEND_ENABLE_HOLD bit(0) // Enabled. Indicates the cycle is active and currently undergoing a hold. #define SUSPEND_HOLD_COMPLETE bit(0) // Indicates initial feed hold is complete.
#define SUSPEND_ENABLE_READY bit(1) // Ready to resume with a cycle start command. #define SUSPEND_RESTART_RETRACT bit(1) // Flag to indicate a retract from a restore parking motion.
#define SUSPEND_ENERGIZE bit(2) // Re-energizes output before resume. #define SUSPEND_RETRACT_COMPLETE bit(2) // (Safety door only) Indicates retraction and de-energizing is complete.
#define SUSPEND_MOTION_CANCEL bit(3) // Cancels resume motion. Used by probing routine. #define SUSPEND_INITIATE_RESTORE bit(3) // (Safety door only) Flag to initiate resume procedures from a cycle start.
#define SUSPEND_RESTORE_COMPLETE bit(4) // (Safety door only) Indicates ready to resume normal operation.
#define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Indicates suspend was initiated by a safety door state.
#define SUSPEND_MOTION_CANCEL bit(6) // Indicates a canceled resume motion. Currently used by probing routine.
#define STEP_CONTROL_NORMAL_OP 0
// #define STEP_CONTROL_RECOMPUTE_ACTIVE_BLOCK bit(0)
#define STEP_CONTROL_END_MOTION bit(1)
#define STEP_CONTROL_EXECUTE_HOLD bit(2)
#define STEP_CONTROL_EXECUTE_PARK bit(3)
// Define global system variables // Define global system variables
@ -72,6 +83,7 @@ typedef struct {
uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t state; // Tracks the current state of Grbl. uint8_t state; // Tracks the current state of Grbl.
uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door. uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door.
uint8_t step_control;
volatile uint8_t rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks. 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. volatile uint8_t rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.