Mass reformat

No code changes other than what clang-format mandates. This is breaking
This commit is contained in:
Imbus 2025-12-28 07:09:00 +01:00
parent a1c041481c
commit fe32d197f9
38 changed files with 7094 additions and 6574 deletions

View file

@ -29,7 +29,6 @@
#define config_h #define config_h
#include "grbl.h" // For Arduino IDE compatibility. #include "grbl.h" // For Arduino IDE compatibility.
// Define CPU pin map and default settings. // Define CPU pin map and default settings.
// NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only // NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only
// one configuration file by placing their specific defaults and pin map at the bottom of this file. // one configuration file by placing their specific defaults and pin map at the bottom of this file.
@ -48,10 +47,10 @@
// g-code programs, maybe selected for interface programs. // g-code programs, maybe selected for interface programs.
// NOTE: If changed, manually update help message in report.c. // NOTE: If changed, manually update help message in report.c.
#define CMD_RESET 0x18 // ctrl-x. #define CMD_RESET 0x18 // ctrl-x.
#define CMD_STATUS_REPORT '?' #define CMD_STATUS_REPORT '?'
#define CMD_CYCLE_START '~' #define CMD_CYCLE_START '~'
#define CMD_FEED_HOLD '!' #define CMD_FEED_HOLD '!'
// NOTE: All override realtime commands must be in the extended ASCII character set, starting // NOTE: All override realtime commands must be in the extended ASCII character set, starting
// at character value 128 (0x80) and up to 255 (0xFF). If the normal set of realtime commands, // at character value 128 (0x80) and up to 255 (0xFF). If the normal set of realtime commands,
@ -61,26 +60,26 @@
// #define CMD_STATUS_REPORT 0x81 // #define CMD_STATUS_REPORT 0x81
// #define CMD_CYCLE_START 0x82 // #define CMD_CYCLE_START 0x82
// #define CMD_FEED_HOLD 0x83 // #define CMD_FEED_HOLD 0x83
#define CMD_SAFETY_DOOR 0x84 #define CMD_SAFETY_DOOR 0x84
#define CMD_JOG_CANCEL 0x85 #define CMD_JOG_CANCEL 0x85
#define CMD_DEBUG_REPORT 0x86 // Only when DEBUG enabled, sends debug report in '{}' braces. #define CMD_DEBUG_REPORT 0x86 // Only when DEBUG enabled, sends debug report in '{}' braces.
#define CMD_FEED_OVR_RESET 0x90 // Restores feed override value to 100%. #define CMD_FEED_OVR_RESET 0x90 // Restores feed override value to 100%.
#define CMD_FEED_OVR_COARSE_PLUS 0x91 #define CMD_FEED_OVR_COARSE_PLUS 0x91
#define CMD_FEED_OVR_COARSE_MINUS 0x92 #define CMD_FEED_OVR_COARSE_MINUS 0x92
#define CMD_FEED_OVR_FINE_PLUS 0x93 #define CMD_FEED_OVR_FINE_PLUS 0x93
#define CMD_FEED_OVR_FINE_MINUS 0x94 #define CMD_FEED_OVR_FINE_MINUS 0x94
#define CMD_RAPID_OVR_RESET 0x95 // Restores rapid override value to 100%. #define CMD_RAPID_OVR_RESET 0x95 // Restores rapid override value to 100%.
#define CMD_RAPID_OVR_MEDIUM 0x96 #define CMD_RAPID_OVR_MEDIUM 0x96
#define CMD_RAPID_OVR_LOW 0x97 #define CMD_RAPID_OVR_LOW 0x97
// #define CMD_RAPID_OVR_EXTRA_LOW 0x98 // *NOT SUPPORTED* // #define CMD_RAPID_OVR_EXTRA_LOW 0x98 // *NOT SUPPORTED*
#define CMD_SPINDLE_OVR_RESET 0x99 // Restores spindle override value to 100%. #define CMD_SPINDLE_OVR_RESET 0x99 // Restores spindle override value to 100%.
#define CMD_SPINDLE_OVR_COARSE_PLUS 0x9A #define CMD_SPINDLE_OVR_COARSE_PLUS 0x9A
#define CMD_SPINDLE_OVR_COARSE_MINUS 0x9B #define CMD_SPINDLE_OVR_COARSE_MINUS 0x9B
#define CMD_SPINDLE_OVR_FINE_PLUS 0x9C #define CMD_SPINDLE_OVR_FINE_PLUS 0x9C
#define CMD_SPINDLE_OVR_FINE_MINUS 0x9D #define CMD_SPINDLE_OVR_FINE_MINUS 0x9D
#define CMD_SPINDLE_OVR_STOP 0x9E #define CMD_SPINDLE_OVR_STOP 0x9E
#define CMD_COOLANT_FLOOD_OVR_TOGGLE 0xA0 #define CMD_COOLANT_FLOOD_OVR_TOGGLE 0xA0
#define CMD_COOLANT_MIST_OVR_TOGGLE 0xA1 #define CMD_COOLANT_MIST_OVR_TOGGLE 0xA1
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces // If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is // the user to perform the homing cycle (or override the locks) before doing anything else. This is
@ -102,12 +101,12 @@
// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits // on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits
// will not be affected by pin sharing. // will not be affected by pin sharing.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // REQUIRED: First move Z to clear workspace. #define HOMING_CYCLE_0 (1 << Z_AXIS) // REQUIRED: First move Z to clear workspace.
#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // OPTIONAL: Then move X,Y at the same time. #define HOMING_CYCLE_1 ((1 << X_AXIS) | (1 << Y_AXIS)) // OPTIONAL: Then move X,Y at the same time.
// #define HOMING_CYCLE_2 // OPTIONAL: Uncomment and add axes mask to enable // #define HOMING_CYCLE_2 // OPTIONAL: Uncomment and add axes mask to enable
// NOTE: The following are two examples to setup homing for 2-axis machines. // NOTE: The following are two examples to setup homing for 2-axis machines.
// #define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS)) // NOT COMPATIBLE WITH COREXY: Homes both X-Y in one cycle. // #define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS)) // NOT COMPATIBLE WITH COREXY: Homes both X-Y in one cycle.
// #define HOMING_CYCLE_0 (1<<X_AXIS) // COREXY COMPATIBLE: First home X // #define HOMING_CYCLE_0 (1<<X_AXIS) // COREXY COMPATIBLE: First home X
// #define HOMING_CYCLE_1 (1<<Y_AXIS) // COREXY COMPATIBLE: Then home Y // #define HOMING_CYCLE_1 (1<<Y_AXIS) // COREXY COMPATIBLE: Then home Y
@ -117,7 +116,7 @@
// greater. // greater.
#define N_HOMING_LOCATE_CYCLE 1 // Integer (1-128) #define N_HOMING_LOCATE_CYCLE 1 // Integer (1-128)
// Enables single axis homing commands. $HX, $HY, and $HZ for X, Y, and Z-axis homing. The full homing // Enables single axis homing commands. $HX, $HY, and $HZ for X, Y, and Z-axis homing. The full homing
// cycle is still invoked by the $H command. This is disabled by default. It's here only to address // cycle is still invoked by the $H command. This is disabled by default. It's here only to address
// users that need to switch between a two-axis and three-axis machine. This is actually very rare. // users that need to switch between a two-axis and three-axis machine. This is actually very rare.
// If you have a two-axis machine, DON'T USE THIS. Instead, just alter the homing cycle for two-axes. // If you have a two-axis machine, DON'T USE THIS. Instead, just alter the homing cycle for two-axes.
@ -238,22 +237,22 @@
// Configure rapid, feed, and spindle override settings. These values define the max and min // Configure rapid, feed, and spindle override settings. These values define the max and min
// allowable override values and the coarse and fine increments per command received. Please // allowable override values and the coarse and fine increments per command received. Please
// note the allowable values in the descriptions following each define. // note the allowable values in the descriptions following each define.
#define DEFAULT_FEED_OVERRIDE 100 // 100%. Don't change this value. #define DEFAULT_FEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-255). Usually 120% or 200% #define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-255). Usually 120% or 200%
#define MIN_FEED_RATE_OVERRIDE 10 // Percent of programmed feed rate (1-100). Usually 50% or 1% #define MIN_FEED_RATE_OVERRIDE 10 // Percent of programmed feed rate (1-100). Usually 50% or 1%
#define FEED_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%. #define FEED_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define FEED_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%. #define FEED_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
#define DEFAULT_RAPID_OVERRIDE 100 // 100%. Don't change this value. #define DEFAULT_RAPID_OVERRIDE 100 // 100%. Don't change this value.
#define RAPID_OVERRIDE_MEDIUM 50 // Percent of rapid (1-99). Usually 50%. #define RAPID_OVERRIDE_MEDIUM 50 // Percent of rapid (1-99). Usually 50%.
#define RAPID_OVERRIDE_LOW 25 // Percent of rapid (1-99). Usually 25%. #define RAPID_OVERRIDE_LOW 25 // Percent of rapid (1-99). Usually 25%.
// #define RAPID_OVERRIDE_EXTRA_LOW 5 // *NOT SUPPORTED* Percent of rapid (1-99). Usually 5%. // #define RAPID_OVERRIDE_EXTRA_LOW 5 // *NOT SUPPORTED* Percent of rapid (1-99). Usually 5%.
#define DEFAULT_SPINDLE_SPEED_OVERRIDE 100 // 100%. Don't change this value. #define DEFAULT_SPINDLE_SPEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_SPINDLE_SPEED_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%. #define MAX_SPINDLE_SPEED_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%.
#define MIN_SPINDLE_SPEED_OVERRIDE 10 // Percent of programmed spindle speed (1-100). Usually 10%. #define MIN_SPINDLE_SPEED_OVERRIDE 10 // Percent of programmed spindle speed (1-100). Usually 10%.
#define SPINDLE_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%. #define SPINDLE_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define SPINDLE_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%. #define SPINDLE_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
// When a M2 or M30 program end command is executed, most g-code states are restored to their defaults. // When a M2 or M30 program end command is executed, most g-code states are restored to their defaults.
// This compile-time option includes the restoring of the feed, rapid, and spindle speed override values // This compile-time option includes the restoring of the feed, rapid, and spindle speed override values
@ -262,16 +261,16 @@
// The status report change for Grbl v1.1 and after also removed the ability to disable/enable most data // The status report change for Grbl v1.1 and after also removed the ability to disable/enable most data
// fields from the report. This caused issues for GUI developers, who've had to manage several scenarios // fields from the report. This caused issues for GUI developers, who've had to manage several scenarios
// and configurations. The increased efficiency of the new reporting style allows for all data fields to // and configurations. The increased efficiency of the new reporting style allows for all data fields to
// be sent without potential performance issues. // be sent without potential performance issues.
// NOTE: The options below are here only provide a way to disable certain data fields if a unique // NOTE: The options below are here only provide a way to disable certain data fields if a unique
// situation demands it, but be aware GUIs may depend on this data. If disabled, it may not be compatible. // situation demands it, but be aware GUIs may depend on this data. If disabled, it may not be compatible.
#define REPORT_FIELD_BUFFER_STATE // Default enabled. Comment to disable. #define REPORT_FIELD_BUFFER_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_PIN_STATE // Default enabled. Comment to disable. #define REPORT_FIELD_PIN_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_CURRENT_FEED_SPEED // Default enabled. Comment to disable. #define REPORT_FIELD_CURRENT_FEED_SPEED // Default enabled. Comment to disable.
#define REPORT_FIELD_WORK_COORD_OFFSET // Default enabled. Comment to disable. #define REPORT_FIELD_WORK_COORD_OFFSET // Default enabled. Comment to disable.
#define REPORT_FIELD_OVERRIDES // Default enabled. Comment to disable. #define REPORT_FIELD_OVERRIDES // Default enabled. Comment to disable.
#define REPORT_FIELD_LINE_NUMBERS // Default enabled. Comment to disable. #define REPORT_FIELD_LINE_NUMBERS // Default enabled. Comment to disable.
// Some status report data isn't necessary for realtime, only intermittently, because the values don't // Some status report data isn't necessary for realtime, only intermittently, because the values don't
// change often. The following macros configures how many times a status report needs to be called before // change often. The following macros configures how many times a status report needs to be called before
@ -282,10 +281,10 @@
// refreshes more often when its not doing anything important. With a good GUI, this data doesn't need // refreshes more often when its not doing anything important. With a good GUI, this data doesn't need
// to be refreshed very often, on the order of a several seconds. // to be refreshed very often, on the order of a several seconds.
// NOTE: WCO refresh must be 2 or greater. OVR refresh must be 1 or greater. // NOTE: WCO refresh must be 2 or greater. OVR refresh must be 1 or greater.
#define REPORT_OVR_REFRESH_BUSY_COUNT 20 // (1-255) #define REPORT_OVR_REFRESH_BUSY_COUNT 20 // (1-255)
#define REPORT_OVR_REFRESH_IDLE_COUNT 10 // (1-255) Must be less than or equal to the busy count #define REPORT_OVR_REFRESH_IDLE_COUNT 10 // (1-255) Must be less than or equal to the busy count
#define REPORT_WCO_REFRESH_BUSY_COUNT 30 // (2-255) #define REPORT_WCO_REFRESH_BUSY_COUNT 30 // (2-255)
#define REPORT_WCO_REFRESH_IDLE_COUNT 10 // (2-255) Must be less than or equal to the busy count #define REPORT_WCO_REFRESH_IDLE_COUNT 10 // (2-255) Must be less than or equal to the busy count
// The temporal resolution of the acceleration management subsystem. A higher number gives smoother // The temporal resolution of the acceleration management subsystem. A higher number gives smoother
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively // acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
@ -301,7 +300,7 @@
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible // frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better // noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
// step smoothing. See stepper.c for more details on the AMASS system works. // step smoothing. See stepper.c for more details on the AMASS system works.
#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable. #define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.
// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error // Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
// check in the settings module to prevent settings values that will exceed this limitation. The maximum // check in the settings module to prevent settings values that will exceed this limitation. The maximum
@ -323,9 +322,9 @@
// normal-open switch and vice versa. // normal-open switch and vice versa.
// NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes. // NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes.
// WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors! // WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors!
//#define DISABLE_LIMIT_PIN_PULL_UP // #define DISABLE_LIMIT_PIN_PULL_UP
//#define DISABLE_PROBE_PIN_PULL_UP // #define DISABLE_PROBE_PIN_PULL_UP
//#define DISABLE_CONTROL_PIN_PULL_UP // #define DISABLE_CONTROL_PIN_PULL_UP
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with // Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// the selected axis with the tool oriented toward the negative direction. In other words, a positive // the selected axis with the tool oriented toward the negative direction. In other words, a positive
@ -353,16 +352,16 @@
// 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
// the spindle direction pin(D13) as a separate spindle enable pin along with spindle speed PWM on pin D11. // the spindle direction pin(D13) as a separate spindle enable pin along with spindle speed PWM on pin D11.
// NOTE: This configure option only works with VARIABLE_SPINDLE enabled and a 328p processor (Uno). // NOTE: This configure option only works with VARIABLE_SPINDLE enabled and a 328p processor (Uno).
// NOTE: Without a direction pin, M4 will not have a pin output to indicate a difference with M3. // NOTE: Without a direction pin, M4 will not have a pin output to indicate a difference with M3.
// NOTE: BEWARE! The Arduino bootloader toggles the D13 pin when it powers up. If you flash Grbl with // NOTE: BEWARE! The Arduino bootloader toggles the D13 pin when it powers up. If you flash Grbl with
// a programmer (you can use a spare Arduino as "Arduino as ISP". Search the web on how to wire this.), // a programmer (you can use a spare Arduino as "Arduino as ISP". Search the web on how to wire this.),
// this D13 LED toggling should go away. We haven't tested this though. Please report how it goes! // this D13 LED toggling should go away. We haven't tested this though. Please report how it goes!
// #define USE_SPINDLE_DIR_AS_ENABLE_PIN // Default disabled. Uncomment to enable. // #define USE_SPINDLE_DIR_AS_ENABLE_PIN // Default disabled. Uncomment to enable.
// Alters the behavior of the spindle enable pin with the USE_SPINDLE_DIR_AS_ENABLE_PIN option . By default, // Alters the behavior of the spindle enable pin with the USE_SPINDLE_DIR_AS_ENABLE_PIN option . By default,
// Grbl will not disable the enable pin if spindle speed is zero and M3/4 is active, but still sets the PWM // Grbl will not disable the enable pin if spindle speed is zero and M3/4 is active, but still sets the PWM
// output to zero. This allows the users to know if the spindle is active and use it as an additional control // output to zero. This allows the users to know if the spindle is active and use it as an additional control
// input. However, in some use cases, user may want the enable pin to disable with a zero spindle speed and // input. However, in some use cases, user may want the enable pin to disable with a zero spindle speed and
// re-enable when spindle speed is greater than zero. This option does that. // re-enable when spindle speed is greater than zero. This option does that.
// NOTE: Requires USE_SPINDLE_DIR_AS_ENABLE_PIN to be enabled. // NOTE: Requires USE_SPINDLE_DIR_AS_ENABLE_PIN to be enabled.
// #define SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED // Default disabled. Uncomment to enable. // #define SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED // Default disabled. Uncomment to enable.
@ -462,12 +461,12 @@
// #define RX_BUFFER_SIZE 128 // (1-254) Uncomment to override defaults in serial.h // #define RX_BUFFER_SIZE 128 // (1-254) Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 100 // (1-254) // #define TX_BUFFER_SIZE 100 // (1-254)
// A simple software debouncing feature for hard limit switches. When enabled, the interrupt // A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check // monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with // the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with // problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// electrical interference on the signal cables from external sources. It's recommended to first // electrical interference on the signal cables from external sources. It's recommended to first
// use shielded signal cables with their shielding connected to ground (old USB/computer cables // use shielded signal cables with their shielding connected to ground (old USB/computer cables
// work well and are cheap to find) and wire in a low-pass circuit into each limit pin. // work well and are cheap to find) and wire in a low-pass circuit into each limit pin.
// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable. // #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.
@ -509,7 +508,8 @@
// written into the Arduino EEPROM via a seperate .INO sketch to contain product data. Altering this // written into the Arduino EEPROM via a seperate .INO sketch to contain product data. Altering this
// macro to not restore the build info EEPROM will ensure this data is retained after firmware upgrades. // macro to not restore the build info EEPROM will ensure this data is retained after firmware upgrades.
// NOTE: Uncomment to override defaults in settings.h // NOTE: Uncomment to override defaults in settings.h
// #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS | SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO) // #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS |
// SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO)
// Enable the '$I=(string)' build info write command. If disabled, any existing build info data must // Enable the '$I=(string)' build info write command. If disabled, any existing build info data must
// be placed into EEPROM via external means with a valid checksum value. This macro option is useful // be placed into EEPROM via external means with a valid checksum value. This macro option is useful
@ -542,8 +542,8 @@
#define FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // Default enabled. Comment to disable. #define FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // Default enabled. Comment to disable.
// By default, Grbl disables feed rate overrides for all G38.x probe cycle commands. Although this // By default, Grbl disables feed rate overrides for all G38.x probe cycle commands. Although this
// may be different than some pro-class machine control, it's arguable that it should be this way. // may be different than some pro-class machine control, it's arguable that it should be this way.
// Most probe sensors produce different levels of error that is dependent on rate of speed. By // Most probe sensors produce different levels of error that is dependent on rate of speed. By
// keeping probing cycles to their programmed feed rates, the probe sensor should be a lot more // keeping probing cycles to their programmed feed rates, the probe sensor should be a lot more
// repeatable. If needed, you can disable this behavior by uncommenting the define below. // repeatable. If needed, you can disable this behavior by uncommenting the define below.
// #define ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES // Default disabled. Uncomment to enable. // #define ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES // Default disabled. Uncomment to enable.
@ -564,18 +564,19 @@
// #define PARKING_ENABLE // Default disabled. Uncomment to enable // #define PARKING_ENABLE // Default disabled. Uncomment to enable
// Configure options for the parking motion, if enabled. // Configure options for the parking motion, if enabled.
#define PARKING_AXIS Z_AXIS // Define which axis that performs the parking motion #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_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
#define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min. #define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min.
#define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min. #define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance. #define PARKING_PULLOUT_INCREMENT \
// Must be positive value or equal to zero. 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
// Must be positive value or equal to zero.
// Enables a special set of M-code commands that enables and disables the parking motion. // Enables a special set of M-code commands that enables and disables the parking motion.
// These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable. // These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable.
// The command is modal and will be set after a planner sync. Since it is g-code, it is // The command is modal and will be set after a planner sync. Since it is g-code, it is
// executed in sync with g-code commands. It is not a real-time command. // executed in sync with g-code commands. It is not a real-time command.
// NOTE: PARKING_ENABLE is required. By default, M56 is active upon initialization. Use // NOTE: PARKING_ENABLE is required. By default, M56 is active upon initialization. Use
// DEACTIVATE_PARKING_UPON_INIT to set M56 P0 as the power-up default. // DEACTIVATE_PARKING_UPON_INIT to set M56 P0 as the power-up default.
// #define ENABLE_PARKING_OVERRIDE_CONTROL // Default disabled. Uncomment to enable // #define ENABLE_PARKING_OVERRIDE_CONTROL // Default disabled. Uncomment to enable
// #define DEACTIVATE_PARKING_UPON_INIT // Default disabled. Uncomment to enable. // #define DEACTIVATE_PARKING_UPON_INIT // Default disabled. Uncomment to enable.
@ -587,7 +588,7 @@
#define DISABLE_LASER_DURING_HOLD // Default enabled. Comment to disable. #define DISABLE_LASER_DURING_HOLD // Default enabled. Comment to disable.
// This feature alters the spindle PWM/speed to a nonlinear output with a simple piecewise linear // This feature alters the spindle PWM/speed to a nonlinear output with a simple piecewise linear
// curve. Useful for spindles that don't produce the right RPM from Grbl's standard spindle PWM // curve. Useful for spindles that don't produce the right RPM from Grbl's standard spindle PWM
// linear model. Requires a solution by the 'fit_nonlinear_spindle.py' script in the /doc/script // linear model. Requires a solution by the 'fit_nonlinear_spindle.py' script in the /doc/script
// folder of the repo. See file comments on how to gather spindle data and run the script to // folder of the repo. See file comments on how to gather spindle data and run the script to
// generate a solution. // generate a solution.
@ -597,41 +598,41 @@
// the 'fit_nonlinear_spindle.py' script solution. Used only when ENABLE_PIECEWISE_LINEAR_SPINDLE // the 'fit_nonlinear_spindle.py' script solution. Used only when ENABLE_PIECEWISE_LINEAR_SPINDLE
// is enabled. Make sure the constant values are exactly the same as the script solution. // is enabled. Make sure the constant values are exactly the same as the script solution.
// NOTE: When N_PIECES < 4, unused RPM_LINE and RPM_POINT defines are not required and omitted. // NOTE: When N_PIECES < 4, unused RPM_LINE and RPM_POINT defines are not required and omitted.
#define N_PIECES 4 // Integer (1-4). Number of piecewise lines used in script solution. #define N_PIECES 4 // Integer (1-4). Number of piecewise lines used in script solution.
#define RPM_MAX 11686.4 // Max RPM of model. $30 > RPM_MAX will be limited to RPM_MAX. #define RPM_MAX 11686.4 // Max RPM of model. $30 > RPM_MAX will be limited to RPM_MAX.
#define RPM_MIN 202.5 // Min RPM of model. $31 < RPM_MIN will be limited to RPM_MIN. #define RPM_MIN 202.5 // Min RPM of model. $31 < RPM_MIN will be limited to RPM_MIN.
#define RPM_POINT12 6145.4 // Used N_PIECES >=2. Junction point between lines 1 and 2. #define RPM_POINT12 6145.4 // Used N_PIECES >=2. Junction point between lines 1 and 2.
#define RPM_POINT23 9627.8 // Used N_PIECES >=3. Junction point between lines 2 and 3. #define RPM_POINT23 9627.8 // Used N_PIECES >=3. Junction point between lines 2 and 3.
#define RPM_POINT34 10813.9 // Used N_PIECES = 4. Junction point between lines 3 and 4. #define RPM_POINT34 10813.9 // Used N_PIECES = 4. Junction point between lines 3 and 4.
#define RPM_LINE_A1 3.197101e-03 // Used N_PIECES >=1. A and B constants of line 1. #define RPM_LINE_A1 3.197101e-03 // Used N_PIECES >=1. A and B constants of line 1.
#define RPM_LINE_B1 -3.526076e-1 #define RPM_LINE_B1 -3.526076e-1
#define RPM_LINE_A2 1.722950e-2 // Used N_PIECES >=2. A and B constants of line 2. #define RPM_LINE_A2 1.722950e-2 // Used N_PIECES >=2. A and B constants of line 2.
#define RPM_LINE_B2 8.588176e+01 #define RPM_LINE_B2 8.588176e+01
#define RPM_LINE_A3 5.901518e-02 // Used N_PIECES >=3. A and B constants of line 3. #define RPM_LINE_A3 5.901518e-02 // Used N_PIECES >=3. A and B constants of line 3.
#define RPM_LINE_B3 4.881851e+02 #define RPM_LINE_B3 4.881851e+02
#define RPM_LINE_A4 1.203413e-01 // Used N_PIECES = 4. A and B constants of line 4. #define RPM_LINE_A4 1.203413e-01 // Used N_PIECES = 4. A and B constants of line 4.
#define RPM_LINE_B4 1.151360e+03 #define RPM_LINE_B4 1.151360e+03
/* --------------------------------------------------------------------------------------- /* ---------------------------------------------------------------------------------------
This optional dual axis feature is primarily for the homing cycle to locate two sides of This optional dual axis feature is primarily for the homing cycle to locate two sides of
a dual-motor gantry independently, i.e. self-squaring. This requires an additional limit a dual-motor gantry independently, i.e. self-squaring. This requires an additional limit
switch for the cloned motor. To self square, both limit switches on the cloned axis must switch for the cloned motor. To self square, both limit switches on the cloned axis must
be physically positioned to trigger when the gantry is square. Highly recommend keeping be physically positioned to trigger when the gantry is square. Highly recommend keeping
the motors always enabled to ensure the gantry stays square with the $1=255 setting. the motors always enabled to ensure the gantry stays square with the $1=255 setting.
For Grbl on the Arduino Uno, the cloned axis limit switch must to be shared with and For Grbl on the Arduino Uno, the cloned axis limit switch must to be shared with and
wired with z-axis limit pin due to the lack of available pins. The homing cycle must home wired with z-axis limit pin due to the lack of available pins. The homing cycle must home
the z-axis and cloned axis in different cycles, which is already the default config. the z-axis and cloned axis in different cycles, which is already the default config.
The dual axis feature works by cloning an axis step output onto another pair of step The dual axis feature works by cloning an axis step output onto another pair of step
and direction pins. The step pulse and direction of the cloned motor can be set and direction pins. The step pulse and direction of the cloned motor can be set
independently of the main axis motor. However to save precious flash and memory, this independently of the main axis motor. However to save precious flash and memory, this
dual axis feature must share the same settings (step/mm, max speed, acceleration) as the dual axis feature must share the same settings (step/mm, max speed, acceleration) as the
parent motor. This is NOT a feature for an independent fourth axis. Only a motor clone. parent motor. This is NOT a feature for an independent fourth axis. Only a motor clone.
WARNING: Make sure to test the directions of your dual axis motors! They must be setup WARNING: Make sure to test the directions of your dual axis motors! They must be setup
to move the same direction BEFORE running your first homing cycle or any long motion! to move the same direction BEFORE running your first homing cycle or any long motion!
Motors moving in opposite directions can cause serious damage to your machine! Use this Motors moving in opposite directions can cause serious damage to your machine! Use this
dual axis feature at your own risk. dual axis feature at your own risk.
*/ */
// NOTE: This feature requires approximately 400 bytes of flash. Certain configurations can // NOTE: This feature requires approximately 400 bytes of flash. Certain configurations can
@ -641,40 +642,39 @@
// #define ENABLE_DUAL_AXIS // Default disabled. Uncomment to enable. // #define ENABLE_DUAL_AXIS // Default disabled. Uncomment to enable.
// Select the one axis to mirror another motor. Only X and Y axis is supported at this time. // Select the one axis to mirror another motor. Only X and Y axis is supported at this time.
#define DUAL_AXIS_SELECT X_AXIS // Must be either X_AXIS or Y_AXIS #define DUAL_AXIS_SELECT X_AXIS // Must be either X_AXIS or Y_AXIS
// To prevent the homing cycle from racking the dual axis, when one limit triggers before the // To prevent the homing cycle from racking the dual axis, when one limit triggers before the
// other due to switch failure or noise, the homing cycle will automatically abort if the second // other due to switch failure or noise, the homing cycle will automatically abort if the second
// motor's limit switch does not trigger within the three distance parameters defined below. // motor's limit switch does not trigger within the three distance parameters defined below.
// Axis length percent will automatically compute a fail distance as a percentage of the max // Axis length percent will automatically compute a fail distance as a percentage of the max
// travel of the other non-dual axis, i.e. if dual axis select is X_AXIS at 5.0%, then the fail // travel of the other non-dual axis, i.e. if dual axis select is X_AXIS at 5.0%, then the fail
// distance will be computed as 5.0% of y-axis max travel. Fail distance max and min are the // distance will be computed as 5.0% of y-axis max travel. Fail distance max and min are the
// limits of how far or little a valid fail distance is. // limits of how far or little a valid fail distance is.
#define DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT 5.0 // Float (percent) #define DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT 5.0 // Float (percent)
#define DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX 25.0 // Float (mm) #define DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX 25.0 // Float (mm)
#define DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN 2.5 // Float (mm) #define DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN 2.5 // Float (mm)
// Dual axis pin configuration currently supports two shields. Uncomment the shield you want, // Dual axis pin configuration currently supports two shields. Uncomment the shield you want,
// and comment out the other one(s). // and comment out the other one(s).
// NOTE: Protoneer CNC Shield v3.51 has A.STP and A.DIR wired to pins A4 and A3 respectively. // NOTE: Protoneer CNC Shield v3.51 has A.STP and A.DIR wired to pins A4 and A3 respectively.
// The variable spindle (i.e. laser mode) build option works and may be enabled or disabled. // The variable spindle (i.e. laser mode) build option works and may be enabled or disabled.
// Coolant pin A3 is moved to D13, replacing spindle direction. // Coolant pin A3 is moved to D13, replacing spindle direction.
#define DUAL_AXIS_CONFIG_PROTONEER_V3_51 // Uncomment to select. Comment other configs. #define DUAL_AXIS_CONFIG_PROTONEER_V3_51 // Uncomment to select. Comment other configs.
// NOTE: Arduino CNC Shield Clone (Originally Protoneer v3.0) has A.STP and A.DIR wired to // NOTE: Arduino CNC Shield Clone (Originally Protoneer v3.0) has A.STP and A.DIR wired to
// D12 and D13, respectively. With the limit pins and stepper enable pin on this same port, // D12 and D13, respectively. With the limit pins and stepper enable pin on this same port,
// the spindle enable pin had to be moved and spindle direction pin deleted. The spindle // the spindle enable pin had to be moved and spindle direction pin deleted. The spindle
// enable pin now resides on A3, replacing coolant enable. Coolant enable is bumped over to // enable pin now resides on A3, replacing coolant enable. Coolant enable is bumped over to
// pin A4. Spindle enable is used far more and this pin setup helps facilitate users to // pin A4. Spindle enable is used far more and this pin setup helps facilitate users to
// integrate this feature without arguably too much work. // integrate this feature without arguably too much work.
// Variable spindle (i.e. laser mode) does NOT work with this shield as configured. While // Variable spindle (i.e. laser mode) does NOT work with this shield as configured. While
// variable spindle technically can work with this shield, it requires too many changes for // variable spindle technically can work with this shield, it requires too many changes for
// most user setups to accomodate. It would best be implemented by sharing all limit switches // most user setups to accomodate. It would best be implemented by sharing all limit switches
// on pins D9/D10 (as [X1,Z]/[X2,Y] or [X,Y2]/[Y1,Z]), home each axis independently, and // on pins D9/D10 (as [X1,Z]/[X2,Y] or [X,Y2]/[Y1,Z]), home each axis independently, and
// updating lots of code to ensure everything is running correctly. // updating lots of code to ensure everything is running correctly.
// #define DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE // Uncomment to select. Comment other configs. // #define DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE // Uncomment to select. Comment other configs.
/* --------------------------------------------------------------------------------------- /* ---------------------------------------------------------------------------------------
OEM Single File Configuration Option OEM Single File Configuration Option
@ -688,5 +688,4 @@
// Paste default settings definitions here. // Paste default settings definitions here.
#endif #endif

View file

@ -20,107 +20,101 @@
#include "grbl.h" #include "grbl.h"
void coolant_init() {
void coolant_init() COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); // Configure as output pin
{ #ifdef ENABLE_M7
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); // Configure as output pin
#ifdef ENABLE_M7
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT); COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
#endif #endif
coolant_stop(); coolant_stop();
} }
// Returns current coolant output state. Overrides may alter it from programmed state. // Returns current coolant output state. Overrides may alter it from programmed state.
uint8_t coolant_get_state() uint8_t coolant_get_state() {
{ uint8_t cl_state = COOLANT_STATE_DISABLE;
uint8_t cl_state = COOLANT_STATE_DISABLE; #ifdef INVERT_COOLANT_FLOOD_PIN
#ifdef INVERT_COOLANT_FLOOD_PIN if (bit_isfalse(COOLANT_FLOOD_PORT, (1 << COOLANT_FLOOD_BIT))) {
if (bit_isfalse(COOLANT_FLOOD_PORT,(1 << COOLANT_FLOOD_BIT))) { #else
#else if (bit_istrue(COOLANT_FLOOD_PORT, (1 << COOLANT_FLOOD_BIT))) {
if (bit_istrue(COOLANT_FLOOD_PORT,(1 << COOLANT_FLOOD_BIT))) { #endif
#endif cl_state |= COOLANT_STATE_FLOOD;
cl_state |= COOLANT_STATE_FLOOD;
}
#ifdef ENABLE_M7
#ifdef INVERT_COOLANT_MIST_PIN
if (bit_isfalse(COOLANT_MIST_PORT,(1 << COOLANT_MIST_BIT))) {
#else
if (bit_istrue(COOLANT_MIST_PORT,(1 << COOLANT_MIST_BIT))) {
#endif
cl_state |= COOLANT_STATE_MIST;
} }
#endif #ifdef ENABLE_M7
return(cl_state); #ifdef INVERT_COOLANT_MIST_PIN
if (bit_isfalse(COOLANT_MIST_PORT, (1 << COOLANT_MIST_BIT))) {
#else
if (bit_istrue(COOLANT_MIST_PORT, (1 << COOLANT_MIST_BIT))) {
#endif
cl_state |= COOLANT_STATE_MIST;
}
#endif
return (cl_state);
} }
// Directly called by coolant_init(), coolant_set_state(), and mc_reset(), which can be at // Directly called by coolant_init(), coolant_set_state(), and mc_reset(), which can be at
// an interrupt-level. No report flag set, but only called by routines that don't need it. // an interrupt-level. No report flag set, but only called by routines that don't need it.
void coolant_stop() void coolant_stop() {
{ #ifdef INVERT_COOLANT_FLOOD_PIN
#ifdef INVERT_COOLANT_FLOOD_PIN
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#else #else
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
#endif #endif
#ifdef ENABLE_M7 #ifdef ENABLE_M7
#ifdef INVERT_COOLANT_MIST_PIN #ifdef INVERT_COOLANT_MIST_PIN
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#else #else
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
#endif #endif
#endif #endif
} }
// Main program only. Immediately sets flood coolant running state and also mist coolant,
// Main program only. Immediately sets flood coolant running state and also mist coolant,
// if enabled. Also sets a flag to report an update to a coolant state. // if enabled. Also sets a flag to report an update to a coolant state.
// Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code // Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code
// parser program end, and g-code parser coolant_sync(). // parser program end, and g-code parser coolant_sync().
void coolant_set_state(uint8_t mode) void coolant_set_state(uint8_t mode) {
{ if (sys.abort) {
if (sys.abort) { return; } // Block during abort. return;
} // Block during abort.
if (mode & COOLANT_FLOOD_ENABLE) {
#ifdef INVERT_COOLANT_FLOOD_PIN if (mode & COOLANT_FLOOD_ENABLE) {
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); #ifdef INVERT_COOLANT_FLOOD_PIN
#else COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); #else
#endif COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
} else { #endif
#ifdef INVERT_COOLANT_FLOOD_PIN } else {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); #ifdef INVERT_COOLANT_FLOOD_PIN
#else COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); #else
#endif COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT);
} #endif
}
#ifdef ENABLE_M7
if (mode & COOLANT_MIST_ENABLE) { #ifdef ENABLE_M7
#ifdef INVERT_COOLANT_MIST_PIN if (mode & COOLANT_MIST_ENABLE) {
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); #ifdef INVERT_COOLANT_MIST_PIN
#else COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); #else
#endif COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
} else { #endif
#ifdef INVERT_COOLANT_MIST_PIN } else {
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); #ifdef INVERT_COOLANT_MIST_PIN
#else COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); #else
#endif COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT);
} #endif
#endif }
#endif
sys.report_ovr_counter = 0; // Set to report change immediately
sys.report_ovr_counter = 0; // Set to report change immediately
} }
// G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails
// G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails
// if an abort or check-mode is active. // if an abort or check-mode is active.
void coolant_sync(uint8_t mode) void coolant_sync(uint8_t mode) {
{ if (sys.state == STATE_CHECK_MODE) {
if (sys.state == STATE_CHECK_MODE) { return; } return;
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program. }
coolant_set_state(mode); protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
coolant_set_state(mode);
} }

View file

@ -21,13 +21,12 @@
#ifndef coolant_control_h #ifndef coolant_control_h
#define coolant_control_h #define coolant_control_h
#define COOLANT_NO_SYNC false #define COOLANT_NO_SYNC false
#define COOLANT_FORCE_SYNC true #define COOLANT_FORCE_SYNC true
#define COOLANT_STATE_DISABLE 0 // Must be zero
#define COOLANT_STATE_FLOOD PL_COND_FLAG_COOLANT_FLOOD
#define COOLANT_STATE_MIST PL_COND_FLAG_COOLANT_MIST
#define COOLANT_STATE_DISABLE 0 // Must be zero
#define COOLANT_STATE_FLOOD PL_COND_FLAG_COOLANT_FLOOD
#define COOLANT_STATE_MIST PL_COND_FLAG_COOLANT_MIST
// Initializes coolant control pins. // Initializes coolant control pins.
void coolant_init(); void coolant_init();

View file

@ -22,230 +22,231 @@
processor types or alternative pin layouts. This version of Grbl officially supports processor types or alternative pin layouts. This version of Grbl officially supports
only the Arduino Mega328p. */ only the Arduino Mega328p. */
#ifndef cpu_map_h #ifndef cpu_map_h
#define cpu_map_h #define cpu_map_h
#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl. #ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl.
// Define serial port pins and interrupt vectors. // Define serial port pins and interrupt vectors.
#define SERIAL_RX USART_RX_vect #define SERIAL_RX USART_RX_vect
#define SERIAL_UDRE USART_UDRE_vect #define SERIAL_UDRE USART_UDRE_vect
// Define step pulse output pins. NOTE: All step bit pins must be on the same port. // Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEP_DDR DDRD #define STEP_DDR DDRD
#define STEP_PORT PORTD #define STEP_PORT PORTD
#define X_STEP_BIT 2 // Uno Digital Pin 2 #define X_STEP_BIT 2 // Uno Digital Pin 2
#define Y_STEP_BIT 3 // Uno Digital Pin 3 #define Y_STEP_BIT 3 // Uno Digital Pin 3
#define Z_STEP_BIT 4 // Uno Digital Pin 4 #define Z_STEP_BIT 4 // Uno Digital Pin 4
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits #define STEP_MASK ((1 << X_STEP_BIT) | (1 << Y_STEP_BIT) | (1 << Z_STEP_BIT)) // All step bits
// Define step direction output pins. NOTE: All direction pins must be on the same port. // Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR DDRD #define DIRECTION_DDR DDRD
#define DIRECTION_PORT PORTD #define DIRECTION_PORT PORTD
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5 #define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6 #define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7 #define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits #define DIRECTION_MASK ((1 << X_DIRECTION_BIT) | (1 << Y_DIRECTION_BIT) | (1 << Z_DIRECTION_BIT)) // All direction bits
// Define stepper driver enable/disable output pin. // Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRB #define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB #define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8 #define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT) #define STEPPERS_DISABLE_MASK (1 << STEPPERS_DISABLE_BIT)
// Define homing/hard limit switch input pins and limit interrupt vectors. // Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL). // NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL).
#define LIMIT_DDR DDRB #define LIMIT_DDR DDRB
#define LIMIT_PIN PINB #define LIMIT_PIN PINB
#define LIMIT_PORT PORTB #define LIMIT_PORT PORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9 #define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10 #define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11. #ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define Z_LIMIT_BIT 4 // Uno Digital Pin 12 #define Z_LIMIT_BIT 4 // Uno Digital Pin 12
#else #else
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11 #define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#endif #endif
#if !defined(ENABLE_DUAL_AXIS) #if !defined(ENABLE_DUAL_AXIS)
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits #define LIMIT_MASK ((1 << X_LIMIT_BIT) | (1 << Y_LIMIT_BIT) | (1 << Z_LIMIT_BIT)) // All limit bits
#endif #endif
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin #define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect #define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register #define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
// Define user-control controls (cycle start, reset, feed hold) input pins. // Define user-control controls (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits). // NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_DDR DDRC #define CONTROL_DDR DDRC
#define CONTROL_PIN PINC #define CONTROL_PIN PINC
#define CONTROL_PORT PORTC #define CONTROL_PORT PORTC
#define CONTROL_RESET_BIT 0 // Uno Analog Pin 0 #define CONTROL_RESET_BIT 0 // Uno Analog Pin 0
#define CONTROL_FEED_HOLD_BIT 1 // Uno Analog Pin 1 #define CONTROL_FEED_HOLD_BIT 1 // Uno Analog Pin 1
#define CONTROL_CYCLE_START_BIT 2 // Uno Analog Pin 2 #define CONTROL_CYCLE_START_BIT 2 // Uno Analog Pin 2
#define CONTROL_SAFETY_DOOR_BIT 1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define. #define CONTROL_SAFETY_DOOR_BIT \
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin 1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_INT_vect PCINT1_vect #define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register #define CONTROL_INT_vect PCINT1_vect
#define CONTROL_MASK ((1<<CONTROL_RESET_BIT)|(1<<CONTROL_FEED_HOLD_BIT)|(1<<CONTROL_CYCLE_START_BIT)|(1<<CONTROL_SAFETY_DOOR_BIT)) #define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins. #define CONTROL_MASK \
((1 << CONTROL_RESET_BIT) | (1 << CONTROL_FEED_HOLD_BIT) | (1 << CONTROL_CYCLE_START_BIT) | \
(1 << CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin. // Define probe switch input pin.
#define PROBE_DDR DDRC #define PROBE_DDR DDRC
#define PROBE_PIN PINC #define PROBE_PIN PINC
#define PROBE_PORT PORTC #define PROBE_PORT PORTC
#define PROBE_BIT 5 // Uno Analog Pin 5 #define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT) #define PROBE_MASK (1 << PROBE_BIT)
#if !defined(ENABLE_DUAL_AXIS) #if !defined(ENABLE_DUAL_AXIS)
// Define flood and mist coolant enable output pins. // Define flood and mist coolant enable output pins.
#define COOLANT_FLOOD_DDR DDRC #define COOLANT_FLOOD_DDR DDRC
#define COOLANT_FLOOD_PORT PORTC #define COOLANT_FLOOD_PORT PORTC
#define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3 #define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3
#define COOLANT_MIST_DDR DDRC #define COOLANT_MIST_DDR DDRC
#define COOLANT_MIST_PORT PORTC #define COOLANT_MIST_PORT PORTC
#define COOLANT_MIST_BIT 4 // Uno Analog Pin 4 #define COOLANT_MIST_BIT 4 // Uno Analog Pin 4
// Define spindle enable and spindle direction output pins. // Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRB #define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB #define SPINDLE_ENABLE_PORT PORTB
// Z Limit pin and spindle PWM/enable pin swapped to access hardware PWM on Pin 11. // Z Limit pin and spindle PWM/enable pin swapped to access hardware PWM on Pin 11.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
// If enabled, spindle direction pin now used as spindle enable, while PWM remains on D11. // If enabled, spindle direction pin now used as spindle enable, while PWM remains on D11.
#define SPINDLE_ENABLE_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.) #define SPINDLE_ENABLE_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#else #else
#define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11 #define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11
#endif #endif
#else #else
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12 #define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#endif #endif
#ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN #ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN
#define SPINDLE_DIRECTION_DDR DDRB #define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB #define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.) #define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13 (NOTE: D13 can't be pulled-high input due to LED.)
#endif #endif
// Variable spindle configuration below. Do not change unless you know what you are doing. // Variable spindle configuration below. Do not change unless you know what you are doing.
// NOTE: Only used when variable spindle is enabled. // NOTE: Only used when variable spindle is enabled.
#define SPINDLE_PWM_MAX_VALUE 255 // Don't change. 328p fast PWM mode fixes top value as 255. #define SPINDLE_PWM_MAX_VALUE 255 // Don't change. 328p fast PWM mode fixes top value as 255.
#ifndef SPINDLE_PWM_MIN_VALUE #ifndef SPINDLE_PWM_MIN_VALUE
#define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero. #define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero.
#endif #endif
#define SPINDLE_PWM_OFF_VALUE 0 #define SPINDLE_PWM_OFF_VALUE 0
#define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE) #define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE - SPINDLE_PWM_MIN_VALUE)
#define SPINDLE_TCCRA_REGISTER TCCR2A #define SPINDLE_TCCRA_REGISTER TCCR2A
#define SPINDLE_TCCRB_REGISTER TCCR2B #define SPINDLE_TCCRB_REGISTER TCCR2B
#define SPINDLE_OCR_REGISTER OCR2A #define SPINDLE_OCR_REGISTER OCR2A
#define SPINDLE_COMB_BIT COM2A1 #define SPINDLE_COMB_BIT COM2A1
// Prescaled, 8-bit Fast PWM mode. // Prescaled, 8-bit Fast PWM mode.
#define SPINDLE_TCCRA_INIT_MASK ((1<<WGM20) | (1<<WGM21)) // Configures fast PWM mode. #define SPINDLE_TCCRA_INIT_MASK ((1 << WGM20) | (1 << WGM21)) // Configures fast PWM mode.
// #define SPINDLE_TCCRB_INIT_MASK (1<<CS20) // Disable prescaler -> 62.5kHz // #define SPINDLE_TCCRB_INIT_MASK (1<<CS20) // Disable prescaler -> 62.5kHz
// #define SPINDLE_TCCRB_INIT_MASK (1<<CS21) // 1/8 prescaler -> 7.8kHz (Used in v0.9) // #define SPINDLE_TCCRB_INIT_MASK (1<<CS21) // 1/8 prescaler -> 7.8kHz (Used in v0.9)
// #define SPINDLE_TCCRB_INIT_MASK ((1<<CS21) | (1<<CS20)) // 1/32 prescaler -> 1.96kHz // #define SPINDLE_TCCRB_INIT_MASK ((1<<CS21) | (1<<CS20)) // 1/32 prescaler -> 1.96kHz
#define SPINDLE_TCCRB_INIT_MASK (1<<CS22) // 1/64 prescaler -> 0.98kHz (J-tech laser) #define SPINDLE_TCCRB_INIT_MASK (1 << CS22) // 1/64 prescaler -> 0.98kHz (J-tech laser)
// 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
#define SPINDLE_PWM_PORT PORTB #define SPINDLE_PWM_PORT PORTB
#define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11 #define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11
#else
// Dual axis feature requires an independent step pulse pin to operate. The independent direction pin is not #else
// absolutely necessary but facilitates easy direction inverting with a Grbl $$ setting. These pins replace
// the spindle direction and optional coolant mist pins.
#ifdef DUAL_AXIS_CONFIG_PROTONEER_V3_51 // Dual axis feature requires an independent step pulse pin to operate. The independent direction pin is not
// NOTE: Step pulse and direction pins may be on any port and output pin. // absolutely necessary but facilitates easy direction inverting with a Grbl $$ setting. These pins replace
#define STEP_DDR_DUAL DDRC // the spindle direction and optional coolant mist pins.
#define STEP_PORT_DUAL PORTC
#define DUAL_STEP_BIT 4 // Uno Analog Pin 4
#define STEP_MASK_DUAL ((1<<DUAL_STEP_BIT))
#define DIRECTION_DDR_DUAL DDRC
#define DIRECTION_PORT_DUAL PORTC
#define DUAL_DIRECTION_BIT 3 // Uno Analog Pin 3
#define DIRECTION_MASK_DUAL ((1<<DUAL_DIRECTION_BIT))
// NOTE: Dual axis limit is shared with the z-axis limit pin by default. Pin used must be on the same port #ifdef DUAL_AXIS_CONFIG_PROTONEER_V3_51
// as other limit pins. // NOTE: Step pulse and direction pins may be on any port and output pin.
#define DUAL_LIMIT_BIT Z_LIMIT_BIT #define STEP_DDR_DUAL DDRC
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)|(1<<DUAL_LIMIT_BIT)) #define STEP_PORT_DUAL PORTC
#define DUAL_STEP_BIT 4 // Uno Analog Pin 4
#define STEP_MASK_DUAL ((1 << DUAL_STEP_BIT))
#define DIRECTION_DDR_DUAL DDRC
#define DIRECTION_PORT_DUAL PORTC
#define DUAL_DIRECTION_BIT 3 // Uno Analog Pin 3
#define DIRECTION_MASK_DUAL ((1 << DUAL_DIRECTION_BIT))
// Define coolant enable output pins. // NOTE: Dual axis limit is shared with the z-axis limit pin by default. Pin used must be on the same port
// NOTE: Coolant flood moved from A3 to A4. Coolant mist not supported with dual axis feature on Arduino Uno. // as other limit pins.
#define COOLANT_FLOOD_DDR DDRB #define DUAL_LIMIT_BIT Z_LIMIT_BIT
#define COOLANT_FLOOD_PORT PORTB #define LIMIT_MASK ((1 << X_LIMIT_BIT) | (1 << Y_LIMIT_BIT) | (1 << Z_LIMIT_BIT) | (1 << DUAL_LIMIT_BIT))
#define COOLANT_FLOOD_BIT 5 // Uno Digital Pin 13
// Define spindle enable output pin. // Define coolant enable output pins.
// NOTE: Spindle enable moved from D12 to A3 (old coolant flood enable pin). Spindle direction pin is removed. // NOTE: Coolant flood moved from A3 to A4. Coolant mist not supported with dual axis feature on Arduino Uno.
#define SPINDLE_ENABLE_DDR DDRB #define COOLANT_FLOOD_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB #define COOLANT_FLOOD_PORT PORTB
#ifdef VARIABLE_SPINDLE #define COOLANT_FLOOD_BIT 5 // Uno Digital Pin 13
// NOTE: USE_SPINDLE_DIR_AS_ENABLE_PIN not supported with dual axis feature.
#define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11
#else
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#endif
// Variable spindle configuration below. Do not change unless you know what you are doing. // Define spindle enable output pin.
// NOTE: Only used when variable spindle is enabled. // NOTE: Spindle enable moved from D12 to A3 (old coolant flood enable pin). Spindle direction pin is removed.
#define SPINDLE_PWM_MAX_VALUE 255 // Don't change. 328p fast PWM mode fixes top value as 255. #define SPINDLE_ENABLE_DDR DDRB
#ifndef SPINDLE_PWM_MIN_VALUE #define SPINDLE_ENABLE_PORT PORTB
#define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero. #ifdef VARIABLE_SPINDLE
#endif // NOTE: USE_SPINDLE_DIR_AS_ENABLE_PIN not supported with dual axis feature.
#define SPINDLE_PWM_OFF_VALUE 0 #define SPINDLE_ENABLE_BIT 3 // Uno Digital Pin 11
#define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE) #else
#define SPINDLE_TCCRA_REGISTER TCCR2A #define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#define SPINDLE_TCCRB_REGISTER TCCR2B #endif
#define SPINDLE_OCR_REGISTER OCR2A
#define SPINDLE_COMB_BIT COM2A1
// Prescaled, 8-bit Fast PWM mode. // Variable spindle configuration below. Do not change unless you know what you are doing.
#define SPINDLE_TCCRA_INIT_MASK ((1<<WGM20) | (1<<WGM21)) // Configures fast PWM mode. // NOTE: Only used when variable spindle is enabled.
// #define SPINDLE_TCCRB_INIT_MASK (1<<CS20) // Disable prescaler -> 62.5kHz #define SPINDLE_PWM_MAX_VALUE 255 // Don't change. 328p fast PWM mode fixes top value as 255.
// #define SPINDLE_TCCRB_INIT_MASK (1<<CS21) // 1/8 prescaler -> 7.8kHz (Used in v0.9) #ifndef SPINDLE_PWM_MIN_VALUE
// #define SPINDLE_TCCRB_INIT_MASK ((1<<CS21) | (1<<CS20)) // 1/32 prescaler -> 1.96kHz #define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero.
#define SPINDLE_TCCRB_INIT_MASK (1<<CS22) // 1/64 prescaler -> 0.98kHz (J-tech laser) #endif
#define SPINDLE_PWM_OFF_VALUE 0
#define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE - SPINDLE_PWM_MIN_VALUE)
#define SPINDLE_TCCRA_REGISTER TCCR2A
#define SPINDLE_TCCRB_REGISTER TCCR2B
#define SPINDLE_OCR_REGISTER OCR2A
#define SPINDLE_COMB_BIT COM2A1
// NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings. // Prescaled, 8-bit Fast PWM mode.
#define SPINDLE_PWM_DDR DDRB #define SPINDLE_TCCRA_INIT_MASK ((1 << WGM20) | (1 << WGM21)) // Configures fast PWM mode.
#define SPINDLE_PWM_PORT PORTB // #define SPINDLE_TCCRB_INIT_MASK (1<<CS20) // Disable prescaler -> 62.5kHz
#define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11 // #define SPINDLE_TCCRB_INIT_MASK (1<<CS21) // 1/8 prescaler -> 7.8kHz (Used in v0.9)
#endif // #define SPINDLE_TCCRB_INIT_MASK ((1<<CS21) | (1<<CS20)) // 1/32 prescaler -> 1.96kHz
#define SPINDLE_TCCRB_INIT_MASK (1 << CS22) // 1/64 prescaler -> 0.98kHz (J-tech laser)
// NOTE: Variable spindle not supported with this shield. // NOTE: On the 328p, these must be the same as the SPINDLE_ENABLE settings.
#ifdef DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE #define SPINDLE_PWM_DDR DDRB
// NOTE: Step pulse and direction pins may be on any port and output pin. #define SPINDLE_PWM_PORT PORTB
#define STEP_DDR_DUAL DDRB #define SPINDLE_PWM_BIT 3 // Uno Digital Pin 11
#define STEP_PORT_DUAL PORTB #endif
#define DUAL_STEP_BIT 4 // Uno Digital Pin 12
#define STEP_MASK_DUAL ((1<<DUAL_STEP_BIT))
#define DIRECTION_DDR_DUAL DDRB
#define DIRECTION_PORT_DUAL PORTB
#define DUAL_DIRECTION_BIT 5 // Uno Digital Pin 13
#define DIRECTION_MASK_DUAL ((1<<DUAL_DIRECTION_BIT))
// NOTE: Dual axis limit is shared with the z-axis limit pin by default. // NOTE: Variable spindle not supported with this shield.
#define DUAL_LIMIT_BIT Z_LIMIT_BIT #ifdef DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)|(1<<DUAL_LIMIT_BIT)) // NOTE: Step pulse and direction pins may be on any port and output pin.
#define STEP_DDR_DUAL DDRB
#define STEP_PORT_DUAL PORTB
#define DUAL_STEP_BIT 4 // Uno Digital Pin 12
#define STEP_MASK_DUAL ((1 << DUAL_STEP_BIT))
#define DIRECTION_DDR_DUAL DDRB
#define DIRECTION_PORT_DUAL PORTB
#define DUAL_DIRECTION_BIT 5 // Uno Digital Pin 13
#define DIRECTION_MASK_DUAL ((1 << DUAL_DIRECTION_BIT))
// Define coolant enable output pins. // NOTE: Dual axis limit is shared with the z-axis limit pin by default.
// NOTE: Coolant flood moved from A3 to A4. Coolant mist not supported with dual axis feature on Arduino Uno. #define DUAL_LIMIT_BIT Z_LIMIT_BIT
#define COOLANT_FLOOD_DDR DDRC #define LIMIT_MASK ((1 << X_LIMIT_BIT) | (1 << Y_LIMIT_BIT) | (1 << Z_LIMIT_BIT) | (1 << DUAL_LIMIT_BIT))
#define COOLANT_FLOOD_PORT PORTC
#define COOLANT_FLOOD_BIT 4 // Uno Analog Pin 4
// Define spindle enable output pin. // Define coolant enable output pins.
// NOTE: Spindle enable moved from D12 to A3 (old coolant flood enable pin). Spindle direction pin is removed. // NOTE: Coolant flood moved from A3 to A4. Coolant mist not supported with dual axis feature on Arduino Uno.
#define SPINDLE_ENABLE_DDR DDRC #define COOLANT_FLOOD_DDR DDRC
#define SPINDLE_ENABLE_PORT PORTC #define COOLANT_FLOOD_PORT PORTC
#define SPINDLE_ENABLE_BIT 3 // Uno Analog Pin 3 #define COOLANT_FLOOD_BIT 4 // Uno Analog Pin 4
#endif
#endif // Define spindle enable output pin.
// NOTE: Spindle enable moved from D12 to A3 (old coolant flood enable pin). Spindle direction pin is removed.
#define SPINDLE_ENABLE_DDR DDRC
#define SPINDLE_ENABLE_PORT PORTC
#define SPINDLE_ENABLE_BIT 3 // Uno Analog Pin 3
#endif
#endif
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,33 +1,33 @@
// This file has been prepared for Doxygen automatic documentation generation. // This file has been prepared for Doxygen automatic documentation generation.
/*! \file ******************************************************************** /*! \file ********************************************************************
* *
* Atmel Corporation * Atmel Corporation
* *
* \li File: eeprom.c * \li File: eeprom.c
* \li Compiler: IAR EWAAVR 3.10c * \li Compiler: IAR EWAAVR 3.10c
* \li Support mail: avr@atmel.com * \li Support mail: avr@atmel.com
* *
* \li Supported devices: All devices with split EEPROM erase/write * \li Supported devices: All devices with split EEPROM erase/write
* capabilities can be used. * capabilities can be used.
* The example is written for ATmega48. * The example is written for ATmega48.
* *
* \li AppNote: AVR103 - Using the EEPROM Programming Modes. * \li AppNote: AVR103 - Using the EEPROM Programming Modes.
* *
* \li Description: Example on how to use the split EEPROM erase/write * \li Description: Example on how to use the split EEPROM erase/write
* capabilities in e.g. ATmega48. All EEPROM * capabilities in e.g. ATmega48. All EEPROM
* programming modes are tested, i.e. Erase+Write, * programming modes are tested, i.e. Erase+Write,
* Erase-only and Write-only. * Erase-only and Write-only.
* *
* $Revision: 1.6 $ * $Revision: 1.6 $
* $Date: Friday, February 11, 2005 07:16:44 UTC $ * $Date: Friday, February 11, 2005 07:16:44 UTC $
****************************************************************************/ ****************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <avr/io.h>
/* These EEPROM bits have different names on different devices. */ /* These EEPROM bits have different names on different devices. */
#ifndef EEPE #ifndef EEPE
#define EEPE EEWE //!< EEPROM program/write enable. #define EEPE EEWE //!< EEPROM program/write enable.
#define EEMPE EEMWE //!< EEPROM master program/write enable. #define EEMPE EEMWE //!< EEPROM master program/write enable.
#endif #endif
/* These two are unfortunately not defined in the device include files. */ /* These two are unfortunately not defined in the device include files. */
@ -46,12 +46,12 @@
* \param addr EEPROM address to read from. * \param addr EEPROM address to read from.
* \return The byte read from the EEPROM address. * \return The byte read from the EEPROM address.
*/ */
unsigned char eeprom_get_char( unsigned int addr ) unsigned char eeprom_get_char(unsigned int addr) {
{ do {
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write. } while (EECR & (1 << EEPE)); // Wait for completion of previous write.
EEAR = addr; // Set EEPROM address register. EEAR = addr; // Set EEPROM address register.
EECR = (1<<EERE); // Start EEPROM read operation. EECR = (1 << EERE); // Start EEPROM read operation.
return EEDR; // Return the byte read from EEPROM. return EEDR; // Return the byte read from EEPROM.
} }
/*! \brief Write byte to EEPROM. /*! \brief Write byte to EEPROM.
@ -71,81 +71,81 @@ unsigned char eeprom_get_char( unsigned int addr )
* \param addr EEPROM address to write to. * \param addr EEPROM address to write to.
* \param new_value New EEPROM value. * \param new_value New EEPROM value.
*/ */
void eeprom_put_char( unsigned int addr, unsigned char new_value ) void eeprom_put_char(unsigned int addr, unsigned char new_value) {
{ char old_value; // Old EEPROM value.
char old_value; // Old EEPROM value. char diff_mask; // Difference mask, i.e. old value XOR new value.
char diff_mask; // Difference mask, i.e. old value XOR new value.
cli(); // Ensure atomic operation for the write operation. cli(); // Ensure atomic operation for the write operation.
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
#ifndef EEPROM_IGNORE_SELFPROG
do {} while( SPMCSR & (1<<SELFPRGEN) ); // Wait for completion of SPM.
#endif
EEAR = addr; // Set EEPROM address register.
EECR = (1<<EERE); // Start EEPROM read operation.
old_value = EEDR; // Get old EEPROM value.
diff_mask = old_value ^ new_value; // Get bit differences.
// Check if any bits are changed to '1' in the new value.
if( diff_mask & new_value ) {
// Now we know that _some_ bits need to be erased to '1'.
// Check if any bits in the new value are '0'.
if( new_value != 0xff ) {
// Now we know that some bits need to be programmed to '0' also.
EEDR = new_value; // Set EEPROM data register.
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
(0<<EEPM1) | (0<<EEPM0); // ...and Erase+Write mode.
EECR |= (1<<EEPE); // Start Erase+Write operation.
} else {
// Now we know that all bits should be erased.
EECR = (1<<EEMPE) | // Set Master Write Enable bit... do {
(1<<EEPM0); // ...and Erase-only mode. } while (EECR & (1 << EEPE)); // Wait for completion of previous write.
EECR |= (1<<EEPE); // Start Erase-only operation. #ifndef EEPROM_IGNORE_SELFPROG
} do {
} else { } while (SPMCSR & (1 << SELFPRGEN)); // Wait for completion of SPM.
// Now we know that _no_ bits need to be erased to '1'. #endif
// Check if any bits are changed from '1' in the old value. EEAR = addr; // Set EEPROM address register.
if( diff_mask ) { EECR = (1 << EERE); // Start EEPROM read operation.
// Now we know that _some_ bits need to the programmed to '0'. old_value = EEDR; // Get old EEPROM value.
diff_mask = old_value ^ new_value; // Get bit differences.
EEDR = new_value; // Set EEPROM data register.
EECR = (1<<EEMPE) | // Set Master Write Enable bit... // Check if any bits are changed to '1' in the new value.
(1<<EEPM1); // ...and Write-only mode. if (diff_mask & new_value) {
EECR |= (1<<EEPE); // Start Write-only operation. // Now we know that _some_ bits need to be erased to '1'.
}
} // Check if any bits in the new value are '0'.
if (new_value != 0xff) {
sei(); // Restore interrupt flag state. // Now we know that some bits need to be programmed to '0' also.
EEDR = new_value; // Set EEPROM data register.
EECR = (1 << EEMPE) | // Set Master Write Enable bit...
(0 << EEPM1) | (0 << EEPM0); // ...and Erase+Write mode.
EECR |= (1 << EEPE); // Start Erase+Write operation.
} else {
// Now we know that all bits should be erased.
EECR = (1 << EEMPE) | // Set Master Write Enable bit...
(1 << EEPM0); // ...and Erase-only mode.
EECR |= (1 << EEPE); // Start Erase-only operation.
}
} else {
// Now we know that _no_ bits need to be erased to '1'.
// Check if any bits are changed from '1' in the old value.
if (diff_mask) {
// Now we know that _some_ bits need to the programmed to '0'.
EEDR = new_value; // Set EEPROM data register.
EECR = (1 << EEMPE) | // Set Master Write Enable bit...
(1 << EEPM1); // ...and Write-only mode.
EECR |= (1 << EEPE); // Start Write-only operation.
}
}
sei(); // Restore interrupt flag state.
} }
// Extensions added as part of Grbl // Extensions added as part of Grbl
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) { void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
unsigned char checksum = 0; unsigned char checksum = 0;
for(; size > 0; size--) { for (; size > 0; size--) {
checksum = ((checksum << 1) != 0) || (checksum >> 7); checksum = ((checksum << 1) != 0) || (checksum >> 7);
checksum += *source; checksum += *source;
eeprom_put_char(destination++, *(source++)); eeprom_put_char(destination++, *(source++));
} }
eeprom_put_char(destination, checksum); eeprom_put_char(destination, checksum);
} }
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) { int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) {
unsigned char data, checksum = 0; unsigned char data, checksum = 0;
for(; size > 0; size--) { for (; size > 0; size--) {
data = eeprom_get_char(source++); data = eeprom_get_char(source++);
checksum = ((checksum << 1) != 0) || (checksum >> 7); checksum = ((checksum << 1) != 0) || (checksum >> 7);
checksum += data; checksum += data;
*(destination++) = data; *(destination++) = data;
} }
return(checksum == eeprom_get_char(source)); return (checksum == eeprom_get_char(source));
} }
// end of file // end of file

View file

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

File diff suppressed because it is too large Load diff

View file

@ -22,59 +22,58 @@
#ifndef gcode_h #ifndef gcode_h
#define gcode_h #define gcode_h
// Define modal group internal numbers for checking multiple command violations and tracking the // Define modal group internal numbers for checking multiple command violations and tracking the
// type of command that is called in the block. A modal group is a group of g-code commands that are // type of command that is called in the block. A modal group is a group of g-code commands that are
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute // mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, // a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). // and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
// NOTE: Modal group define values must be sequential and starting from zero. // NOTE: Modal group define values must be sequential and starting from zero.
#define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal #define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal
#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion #define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion
#define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection #define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection
#define MODAL_GROUP_G3 3 // [G90,G91] Distance mode #define MODAL_GROUP_G3 3 // [G90,G91] Distance mode
#define MODAL_GROUP_G4 4 // [G91.1] Arc IJK distance mode #define MODAL_GROUP_G4 4 // [G91.1] Arc IJK distance mode
#define MODAL_GROUP_G5 5 // [G93,G94] Feed rate mode #define MODAL_GROUP_G5 5 // [G93,G94] Feed rate mode
#define MODAL_GROUP_G6 6 // [G20,G21] Units #define MODAL_GROUP_G6 6 // [G20,G21] Units
#define MODAL_GROUP_G7 7 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED. #define MODAL_GROUP_G7 7 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED.
#define MODAL_GROUP_G8 8 // [G43.1,G49] Tool length offset #define MODAL_GROUP_G8 8 // [G43.1,G49] Tool length offset
#define MODAL_GROUP_G12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection #define MODAL_GROUP_G12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
#define MODAL_GROUP_G13 10 // [G61] Control mode #define MODAL_GROUP_G13 10 // [G61] Control mode
#define MODAL_GROUP_M4 11 // [M0,M1,M2,M30] Stopping #define MODAL_GROUP_M4 11 // [M0,M1,M2,M30] Stopping
#define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning #define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning
#define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control #define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control
#define MODAL_GROUP_M9 14 // [M56] Override control #define MODAL_GROUP_M9 14 // [M56] Override control
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute. // internally by the parser to know which command to execute.
// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing // NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing
// compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not // compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not
// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c // ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c
// to see how they are used, if you need to alter them. // to see how they are used, if you need to alter them.
// Modal Group G0: Non-modal actions // Modal Group G0: Non-modal actions
#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero) #define NON_MODAL_NO_ACTION 0 // (Default: Must be zero)
#define NON_MODAL_DWELL 4 // G4 (Do not alter value) #define NON_MODAL_DWELL 4 // G4 (Do not alter value)
#define NON_MODAL_SET_COORDINATE_DATA 10 // G10 (Do not alter value) #define NON_MODAL_SET_COORDINATE_DATA 10 // G10 (Do not alter value)
#define NON_MODAL_GO_HOME_0 28 // G28 (Do not alter value) #define NON_MODAL_GO_HOME_0 28 // G28 (Do not alter value)
#define NON_MODAL_SET_HOME_0 38 // G28.1 (Do not alter value) #define NON_MODAL_SET_HOME_0 38 // G28.1 (Do not alter value)
#define NON_MODAL_GO_HOME_1 30 // G30 (Do not alter value) #define NON_MODAL_GO_HOME_1 30 // G30 (Do not alter value)
#define NON_MODAL_SET_HOME_1 40 // G30.1 (Do not alter value) #define NON_MODAL_SET_HOME_1 40 // G30.1 (Do not alter value)
#define NON_MODAL_ABSOLUTE_OVERRIDE 53 // G53 (Do not alter value) #define NON_MODAL_ABSOLUTE_OVERRIDE 53 // G53 (Do not alter value)
#define NON_MODAL_SET_COORDINATE_OFFSET 92 // G92 (Do not alter value) #define NON_MODAL_SET_COORDINATE_OFFSET 92 // G92 (Do not alter value)
#define NON_MODAL_RESET_COORDINATE_OFFSET 102 //G92.1 (Do not alter value) #define NON_MODAL_RESET_COORDINATE_OFFSET 102 // G92.1 (Do not alter value)
// Modal Group G1: Motion modes // Modal Group G1: Motion modes
#define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero) #define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero)
#define MOTION_MODE_LINEAR 1 // G1 (Do not alter value) #define MOTION_MODE_LINEAR 1 // G1 (Do not alter value)
#define MOTION_MODE_CW_ARC 2 // G2 (Do not alter value) #define MOTION_MODE_CW_ARC 2 // G2 (Do not alter value)
#define MOTION_MODE_CCW_ARC 3 // G3 (Do not alter value) #define MOTION_MODE_CCW_ARC 3 // G3 (Do not alter value)
#define MOTION_MODE_PROBE_TOWARD 140 // G38.2 (Do not alter value) #define MOTION_MODE_PROBE_TOWARD 140 // G38.2 (Do not alter value)
#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 141 // G38.3 (Do not alter value) #define MOTION_MODE_PROBE_TOWARD_NO_ERROR 141 // G38.3 (Do not alter value)
#define MOTION_MODE_PROBE_AWAY 142 // G38.4 (Do not alter value) #define MOTION_MODE_PROBE_AWAY 142 // G38.4 (Do not alter value)
#define MOTION_MODE_PROBE_AWAY_NO_ERROR 143 // G38.5 (Do not alter value) #define MOTION_MODE_PROBE_AWAY_NO_ERROR 143 // G38.5 (Do not alter value)
#define MOTION_MODE_NONE 80 // G80 (Do not alter value) #define MOTION_MODE_NONE 80 // G80 (Do not alter value)
// Modal Group G2: Plane select // Modal Group G2: Plane select
#define PLANE_SELECT_XY 0 // G17 (Default: Must be zero) #define PLANE_SELECT_XY 0 // G17 (Default: Must be zero)
@ -82,25 +81,25 @@
#define PLANE_SELECT_YZ 2 // G19 (Do not alter value) #define PLANE_SELECT_YZ 2 // G19 (Do not alter value)
// Modal Group G3: Distance mode // Modal Group G3: Distance mode
#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero) #define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero)
#define DISTANCE_MODE_INCREMENTAL 1 // G91 (Do not alter value) #define DISTANCE_MODE_INCREMENTAL 1 // G91 (Do not alter value)
// Modal Group G4: Arc IJK distance mode // Modal Group G4: Arc IJK distance mode
#define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero) #define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero)
// Modal Group M4: Program flow // Modal Group M4: Program flow
#define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero) #define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero)
#define PROGRAM_FLOW_PAUSED 3 // M0 #define PROGRAM_FLOW_PAUSED 3 // M0
#define PROGRAM_FLOW_OPTIONAL_STOP 1 // M1 NOTE: Not supported, but valid and ignored. #define PROGRAM_FLOW_OPTIONAL_STOP 1 // M1 NOTE: Not supported, but valid and ignored.
#define PROGRAM_FLOW_COMPLETED_M2 2 // M2 (Do not alter value) #define PROGRAM_FLOW_COMPLETED_M2 2 // M2 (Do not alter value)
#define PROGRAM_FLOW_COMPLETED_M30 30 // M30 (Do not alter value) #define PROGRAM_FLOW_COMPLETED_M30 30 // M30 (Do not alter value)
// Modal Group G5: Feed rate mode // Modal Group G5: Feed rate mode
#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero) #define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero)
#define FEED_RATE_MODE_INVERSE_TIME 1 // G93 (Do not alter value) #define FEED_RATE_MODE_INVERSE_TIME 1 // G93 (Do not alter value)
// Modal Group G6: Units mode // Modal Group G6: Units mode
#define UNITS_MODE_MM 0 // G21 (Default: Must be zero) #define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
#define UNITS_MODE_INCHES 1 // G20 (Do not alter value) #define UNITS_MODE_INCHES 1 // G20 (Do not alter value)
// Modal Group G7: Cutter radius compensation mode // Modal Group G7: Cutter radius compensation mode
@ -110,132 +109,129 @@
#define CONTROL_MODE_EXACT_PATH 0 // G61 (Default: Must be zero) #define CONTROL_MODE_EXACT_PATH 0 // G61 (Default: Must be zero)
// Modal Group M7: Spindle control // Modal Group M7: Spindle control
#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero) #define SPINDLE_DISABLE 0 // M5 (Default: Must be zero)
#define SPINDLE_ENABLE_CW PL_COND_FLAG_SPINDLE_CW // M3 (NOTE: Uses planner condition bit flag) #define SPINDLE_ENABLE_CW PL_COND_FLAG_SPINDLE_CW // M3 (NOTE: Uses planner condition bit flag)
#define SPINDLE_ENABLE_CCW PL_COND_FLAG_SPINDLE_CCW // M4 (NOTE: Uses planner condition bit flag) #define SPINDLE_ENABLE_CCW PL_COND_FLAG_SPINDLE_CCW // M4 (NOTE: Uses planner condition bit flag)
// Modal Group M8: Coolant control // Modal Group M8: Coolant control
#define COOLANT_DISABLE 0 // M9 (Default: Must be zero) #define COOLANT_DISABLE 0 // M9 (Default: Must be zero)
#define COOLANT_FLOOD_ENABLE PL_COND_FLAG_COOLANT_FLOOD // M8 (NOTE: Uses planner condition bit flag) #define COOLANT_FLOOD_ENABLE PL_COND_FLAG_COOLANT_FLOOD // M8 (NOTE: Uses planner condition bit flag)
#define COOLANT_MIST_ENABLE PL_COND_FLAG_COOLANT_MIST // M7 (NOTE: Uses planner condition bit flag) #define COOLANT_MIST_ENABLE PL_COND_FLAG_COOLANT_MIST // M7 (NOTE: Uses planner condition bit flag)
// Modal Group G8: Tool length offset // Modal Group G8: Tool length offset
#define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero) #define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero)
#define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1 #define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1
// Modal Group M9: Override control // Modal Group M9: Override control
#ifdef DEACTIVATE_PARKING_UPON_INIT #ifdef DEACTIVATE_PARKING_UPON_INIT
#define OVERRIDE_DISABLED 0 // (Default: Must be zero) #define OVERRIDE_DISABLED 0 // (Default: Must be zero)
#define OVERRIDE_PARKING_MOTION 1 // M56 #define OVERRIDE_PARKING_MOTION 1 // M56
#else #else
#define OVERRIDE_PARKING_MOTION 0 // M56 (Default: Must be zero) #define OVERRIDE_PARKING_MOTION 0 // M56 (Default: Must be zero)
#define OVERRIDE_DISABLED 1 // Parking disabled. #define OVERRIDE_DISABLED 1 // Parking disabled.
#endif #endif
// Modal Group G12: Active work coordinate system // Modal Group G12: Active work coordinate system
// N/A: Stores coordinate system value (54-59) to change to. // N/A: Stores coordinate system value (54-59) to change to.
// Define parameter word mapping. // Define parameter word mapping.
#define WORD_F 0 #define WORD_F 0
#define WORD_I 1 #define WORD_I 1
#define WORD_J 2 #define WORD_J 2
#define WORD_K 3 #define WORD_K 3
#define WORD_L 4 #define WORD_L 4
#define WORD_N 5 #define WORD_N 5
#define WORD_P 6 #define WORD_P 6
#define WORD_R 7 #define WORD_R 7
#define WORD_S 8 #define WORD_S 8
#define WORD_T 9 #define WORD_T 9
#define WORD_X 10 #define WORD_X 10
#define WORD_Y 11 #define WORD_Y 11
#define WORD_Z 12 #define WORD_Z 12
// Define g-code parser position updating flags // Define g-code parser position updating flags
#define GC_UPDATE_POS_TARGET 0 // Must be zero #define GC_UPDATE_POS_TARGET 0 // Must be zero
#define GC_UPDATE_POS_SYSTEM 1 #define GC_UPDATE_POS_SYSTEM 1
#define GC_UPDATE_POS_NONE 2 #define GC_UPDATE_POS_NONE 2
// Define probe cycle exit states and assign proper position updating. // Define probe cycle exit states and assign proper position updating.
#define GC_PROBE_FOUND GC_UPDATE_POS_SYSTEM #define GC_PROBE_FOUND GC_UPDATE_POS_SYSTEM
#define GC_PROBE_ABORT GC_UPDATE_POS_NONE #define GC_PROBE_ABORT GC_UPDATE_POS_NONE
#define GC_PROBE_FAIL_INIT GC_UPDATE_POS_NONE #define GC_PROBE_FAIL_INIT GC_UPDATE_POS_NONE
#define GC_PROBE_FAIL_END GC_UPDATE_POS_TARGET #define GC_PROBE_FAIL_END GC_UPDATE_POS_TARGET
#ifdef SET_CHECK_MODE_PROBE_TO_START #ifdef SET_CHECK_MODE_PROBE_TO_START
#define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE
#else #else
#define GC_PROBE_CHECK_MODE GC_UPDATE_POS_TARGET #define GC_PROBE_CHECK_MODE GC_UPDATE_POS_TARGET
#endif #endif
// Define gcode parser flags for handling special cases. // Define gcode parser flags for handling special cases.
#define GC_PARSER_NONE 0 // Must be zero. #define GC_PARSER_NONE 0 // Must be zero.
#define GC_PARSER_JOG_MOTION bit(0) #define GC_PARSER_JOG_MOTION bit(0)
#define GC_PARSER_CHECK_MANTISSA bit(1) #define GC_PARSER_CHECK_MANTISSA bit(1)
#define GC_PARSER_ARC_IS_CLOCKWISE bit(2) #define GC_PARSER_ARC_IS_CLOCKWISE bit(2)
#define GC_PARSER_PROBE_IS_AWAY bit(3) #define GC_PARSER_PROBE_IS_AWAY bit(3)
#define GC_PARSER_PROBE_IS_NO_ERROR bit(4) #define GC_PARSER_PROBE_IS_NO_ERROR bit(4)
#define GC_PARSER_LASER_FORCE_SYNC bit(5) #define GC_PARSER_LASER_FORCE_SYNC bit(5)
#define GC_PARSER_LASER_DISABLE bit(6) #define GC_PARSER_LASER_DISABLE bit(6)
#define GC_PARSER_LASER_ISMOTION bit(7) #define GC_PARSER_LASER_ISMOTION bit(7)
// NOTE: When this struct is zeroed, the above defines set the defaults for the system. // NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct { typedef struct {
uint8_t motion; // {G0,G1,G2,G3,G38.2,G80} uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
uint8_t feed_rate; // {G93,G94} uint8_t feed_rate; // {G93,G94}
uint8_t units; // {G20,G21} uint8_t units; // {G20,G21}
uint8_t distance; // {G90,G91} uint8_t distance; // {G90,G91}
// uint8_t distance_arc; // {G91.1} NOTE: Don't track. Only default supported. // uint8_t distance_arc; // {G91.1} NOTE: Don't track. Only default supported.
uint8_t plane_select; // {G17,G18,G19} uint8_t plane_select; // {G17,G18,G19}
// uint8_t cutter_comp; // {G40} NOTE: Don't track. Only default supported. // uint8_t cutter_comp; // {G40} NOTE: Don't track. Only default supported.
uint8_t tool_length; // {G43.1,G49} uint8_t tool_length; // {G43.1,G49}
uint8_t coord_select; // {G54,G55,G56,G57,G58,G59} uint8_t coord_select; // {G54,G55,G56,G57,G58,G59}
// uint8_t control; // {G61} NOTE: Don't track. Only default supported. // uint8_t control; // {G61} NOTE: Don't track. Only default supported.
uint8_t program_flow; // {M0,M1,M2,M30} uint8_t program_flow; // {M0,M1,M2,M30}
uint8_t coolant; // {M7,M8,M9} uint8_t coolant; // {M7,M8,M9}
uint8_t spindle; // {M3,M4,M5} uint8_t spindle; // {M3,M4,M5}
uint8_t override; // {M56} uint8_t override; // {M56}
} gc_modal_t; } gc_modal_t;
typedef struct { typedef struct {
float f; // Feed float f; // Feed
float ijk[3]; // I,J,K Axis arc offsets float ijk[3]; // I,J,K Axis arc offsets
uint8_t l; // G10 or canned cycles parameters uint8_t l; // G10 or canned cycles parameters
int32_t n; // Line number int32_t n; // Line number
float p; // G10 or dwell parameters float p; // G10 or dwell parameters
// float q; // G82 peck drilling // float q; // G82 peck drilling
float r; // Arc radius float r; // Arc radius
float s; // Spindle speed float s; // Spindle speed
uint8_t t; // Tool selection uint8_t t; // Tool selection
float xyz[3]; // X,Y,Z Translational axes float xyz[3]; // X,Y,Z Translational axes
} gc_values_t; } gc_values_t;
typedef struct { typedef struct {
gc_modal_t modal; gc_modal_t modal;
float spindle_speed; // RPM float spindle_speed; // RPM
float feed_rate; // Millimeters/min float feed_rate; // Millimeters/min
uint8_t tool; // Tracks tool number. NOT USED. uint8_t tool; // Tracks tool number. NOT USED.
int32_t line_number; // Last line number sent int32_t line_number; // Last line number sent
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
// position in mm. Loaded from EEPROM when called. // position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot. // machine zero in mm. Non-persistent. Cleared upon reset and boot.
float tool_length_offset; // Tracks tool length offset value when enabled. float tool_length_offset; // Tracks tool length offset value when enabled.
} parser_state_t; } parser_state_t;
extern parser_state_t gc_state; extern parser_state_t gc_state;
typedef struct { typedef struct {
uint8_t non_modal_command; uint8_t non_modal_command;
gc_modal_t modal; gc_modal_t modal;
gc_values_t values; gc_values_t values;
} parser_block_t; } parser_block_t;
// Initialize the parser // Initialize the parser
void gc_init(); void gc_init();

View file

@ -22,117 +22,116 @@
#define grbl_h #define grbl_h
// Grbl versioning system // Grbl versioning system
#define GRBL_VERSION "1.1h" #define GRBL_VERSION "1.1h"
#define GRBL_VERSION_BUILD "20190830" #define GRBL_VERSION_BUILD "20190830"
// Define standard libraries used by Grbl. // Define standard libraries used by Grbl.
#include <avr/interrupt.h>
#include <avr/io.h> #include <avr/io.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h> #include <avr/wdt.h>
#include <util/delay.h>
#include <math.h>
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>
// Define the Grbl system include files. NOTE: Do not alter organization. // Define the Grbl system include files. NOTE: Do not alter organization.
#include "config.h" #include "config.h"
#include "nuts_bolts.h"
#include "settings.h"
#include "system.h"
#include "defaults.h"
#include "cpu_map.h"
#include "planner.h"
#include "coolant_control.h" #include "coolant_control.h"
#include "cpu_map.h"
#include "defaults.h"
#include "eeprom.h" #include "eeprom.h"
#include "gcode.h" #include "gcode.h"
#include "jog.h"
#include "limits.h" #include "limits.h"
#include "motion_control.h" #include "motion_control.h"
#include "nuts_bolts.h"
#include "planner.h" #include "planner.h"
#include "print.h" #include "print.h"
#include "probe.h" #include "probe.h"
#include "protocol.h" #include "protocol.h"
#include "report.h" #include "report.h"
#include "serial.h" #include "serial.h"
#include "settings.h"
#include "spindle_control.h" #include "spindle_control.h"
#include "stepper.h" #include "stepper.h"
#include "jog.h" #include "system.h"
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES: // COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:
#ifndef HOMING_CYCLE_0 #ifndef HOMING_CYCLE_0
#error "Required HOMING_CYCLE_0 not defined." #error "Required HOMING_CYCLE_0 not defined."
#endif #endif
#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(VARIABLE_SPINDLE) #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(VARIABLE_SPINDLE)
#error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with VARIABLE_SPINDLE enabled" #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with VARIABLE_SPINDLE enabled"
#endif #endif
#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(CPU_MAP_ATMEGA328P) #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(CPU_MAP_ATMEGA328P)
#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(USE_SPINDLE_DIR_AS_ENABLE_PIN) && defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED) #if !defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)
#error "SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED may only be used with USE_SPINDLE_DIR_AS_ENABLE_PIN enabled" #error "SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED may only be used with USE_SPINDLE_DIR_AS_ENABLE_PIN enabled"
#endif #endif
#if defined(PARKING_ENABLE) #if defined(PARKING_ENABLE)
#if defined(HOMING_FORCE_SET_ORIGIN) #if defined(HOMING_FORCE_SET_ORIGIN)
#error "HOMING_FORCE_SET_ORIGIN is not supported with PARKING_ENABLE at this time." #error "HOMING_FORCE_SET_ORIGIN is not supported with PARKING_ENABLE at this time."
#endif #endif
#endif #endif
#if defined(ENABLE_PARKING_OVERRIDE_CONTROL) #if defined(ENABLE_PARKING_OVERRIDE_CONTROL)
#if !defined(PARKING_ENABLE) #if !defined(PARKING_ENABLE)
#error "ENABLE_PARKING_OVERRIDE_CONTROL must be enabled with PARKING_ENABLE." #error "ENABLE_PARKING_OVERRIDE_CONTROL must be enabled with PARKING_ENABLE."
#endif #endif
#endif #endif
#if defined(SPINDLE_PWM_MIN_VALUE) #if defined(SPINDLE_PWM_MIN_VALUE)
#if !(SPINDLE_PWM_MIN_VALUE > 0) #if !(SPINDLE_PWM_MIN_VALUE > 0)
#error "SPINDLE_PWM_MIN_VALUE must be greater than zero." #error "SPINDLE_PWM_MIN_VALUE must be greater than zero."
#endif #endif
#endif #endif
#if (REPORT_WCO_REFRESH_BUSY_COUNT < REPORT_WCO_REFRESH_IDLE_COUNT) #if (REPORT_WCO_REFRESH_BUSY_COUNT < REPORT_WCO_REFRESH_IDLE_COUNT)
#error "WCO busy refresh is less than idle refresh." #error "WCO busy refresh is less than idle refresh."
#endif #endif
#if (REPORT_OVR_REFRESH_BUSY_COUNT < REPORT_OVR_REFRESH_IDLE_COUNT) #if (REPORT_OVR_REFRESH_BUSY_COUNT < REPORT_OVR_REFRESH_IDLE_COUNT)
#error "Override busy refresh is less than idle refresh." #error "Override busy refresh is less than idle refresh."
#endif #endif
#if (REPORT_WCO_REFRESH_IDLE_COUNT < 2) #if (REPORT_WCO_REFRESH_IDLE_COUNT < 2)
#error "WCO refresh must be greater than one." #error "WCO refresh must be greater than one."
#endif #endif
#if (REPORT_OVR_REFRESH_IDLE_COUNT < 1) #if (REPORT_OVR_REFRESH_IDLE_COUNT < 1)
#error "Override refresh must be greater than zero." #error "Override refresh must be greater than zero."
#endif #endif
#if defined(ENABLE_DUAL_AXIS) #if defined(ENABLE_DUAL_AXIS)
#if !((DUAL_AXIS_SELECT == X_AXIS) || (DUAL_AXIS_SELECT == Y_AXIS)) #if !((DUAL_AXIS_SELECT == X_AXIS) || (DUAL_AXIS_SELECT == Y_AXIS))
#error "Dual axis currently supports X or Y axes only." #error "Dual axis currently supports X or Y axes only."
#endif #endif
#if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(VARIABLE_SPINDLE) #if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(VARIABLE_SPINDLE)
#error "VARIABLE_SPINDLE not supported with DUAL_AXIS_CNC_SHIELD_CLONE." #error "VARIABLE_SPINDLE not supported with DUAL_AXIS_CNC_SHIELD_CLONE."
#endif #endif
#if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51) #if defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51)
#error "More than one dual axis configuration found. Select one." #error "More than one dual axis configuration found. Select one."
#endif #endif
#if !defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && !defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51) #if !defined(DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE) && !defined(DUAL_AXIS_CONFIG_PROTONEER_V3_51)
#error "No supported dual axis configuration found. Select one." #error "No supported dual axis configuration found. Select one."
#endif #endif
#if defined(COREXY) #if defined(COREXY)
#error "CORE XY not supported with dual axis feature." #error "CORE XY not supported with dual axis feature."
#endif #endif
#if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#error "USE_SPINDLE_DIR_AS_ENABLE_PIN not supported with dual axis feature." #error "USE_SPINDLE_DIR_AS_ENABLE_PIN not supported with dual axis feature."
#endif #endif
#if defined(ENABLE_M7) #if defined(ENABLE_M7)
#error "ENABLE_M7 not supported with dual axis feature." #error "ENABLE_M7 not supported with dual axis feature."
#endif #endif
#endif #endif
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------

View file

@ -20,31 +20,31 @@
#include "grbl.h" #include "grbl.h"
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog. // Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block) uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block) {
{ // Initialize planner data struct for jogging motions.
// Initialize planner data struct for jogging motions. // NOTE: Spindle and coolant are allowed to fully function with overrides during a jog.
// NOTE: Spindle and coolant are allowed to fully function with overrides during a jog. pl_data->feed_rate = gc_block->values.f;
pl_data->feed_rate = gc_block->values.f; pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE;
pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE; #ifdef USE_LINE_NUMBERS
#ifdef USE_LINE_NUMBERS
pl_data->line_number = gc_block->values.n; pl_data->line_number = gc_block->values.n;
#endif #endif
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) {
if (system_check_travel_limits(gc_block->values.xyz)) { return(STATUS_TRAVEL_EXCEEDED); } if (system_check_travel_limits(gc_block->values.xyz)) {
} return (STATUS_TRAVEL_EXCEEDED);
}
// Valid jog command. Plan, set state, and execute.
mc_line(gc_block->values.xyz,pl_data);
if (sys.state == STATE_IDLE) {
if (plan_get_current_block() != NULL) { // Check if there is a block to execute.
sys.state = STATE_JOG;
st_prep_buffer();
st_wake_up(); // NOTE: Manual start. No state machine required.
} }
}
return(STATUS_OK); // Valid jog command. Plan, set state, and execute.
mc_line(gc_block->values.xyz, pl_data);
if (sys.state == STATE_IDLE) {
if (plan_get_current_block() != NULL) { // Check if there is a block to execute.
sys.state = STATE_JOG;
st_prep_buffer();
st_wake_up(); // NOTE: Manual start. No state machine required.
}
}
return (STATUS_OK);
} }

View file

@ -21,130 +21,134 @@
#include "grbl.h" #include "grbl.h"
// Homing axis search distance multiplier. Computed by this value times the cycle travel. // Homing axis search distance multiplier. Computed by this value times the cycle travel.
#ifndef HOMING_AXIS_SEARCH_SCALAR #ifndef HOMING_AXIS_SEARCH_SCALAR
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged. #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
#endif #endif
#ifndef HOMING_AXIS_LOCATE_SCALAR #ifndef HOMING_AXIS_LOCATE_SCALAR
#define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared. #define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared.
#endif #endif
#ifdef ENABLE_DUAL_AXIS #ifdef ENABLE_DUAL_AXIS
// Flags for dual axis async limit trigger check. // Flags for dual axis async limit trigger check.
#define DUAL_AXIS_CHECK_DISABLE 0 // Must be zero #define DUAL_AXIS_CHECK_DISABLE 0 // Must be zero
#define DUAL_AXIS_CHECK_ENABLE bit(0) #define DUAL_AXIS_CHECK_ENABLE bit(0)
#define DUAL_AXIS_CHECK_TRIGGER_1 bit(1) #define DUAL_AXIS_CHECK_TRIGGER_1 bit(1)
#define DUAL_AXIS_CHECK_TRIGGER_2 bit(2) #define DUAL_AXIS_CHECK_TRIGGER_2 bit(2)
#endif #endif
void limits_init() void limits_init() {
{ LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
#ifdef DISABLE_LIMIT_PIN_PULL_UP #ifdef DISABLE_LIMIT_PIN_PULL_UP
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down. LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
#else #else
LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
#endif #endif
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt
} else { } else {
limits_disable(); limits_disable();
} }
#ifdef ENABLE_SOFTWARE_DEBOUNCE #ifdef ENABLE_SOFTWARE_DEBOUNCE
MCUSR &= ~(1<<WDRF); MCUSR &= ~(1 << WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE); WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = (1<<WDP0); // Set time-out at ~32msec. WDTCSR = (1 << WDP0); // Set time-out at ~32msec.
#endif #endif
} }
// Disables hard limits. // Disables hard limits.
void limits_disable() void limits_disable() {
{ LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
} }
// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where // Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their // triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their
// number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1. // number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1.
uint8_t limits_get_state() uint8_t limits_get_state() {
{ uint8_t limit_state = 0;
uint8_t limit_state = 0; uint8_t pin = (LIMIT_PIN & LIMIT_MASK);
uint8_t pin = (LIMIT_PIN & LIMIT_MASK); #ifdef INVERT_LIMIT_PIN_MASK
#ifdef INVERT_LIMIT_PIN_MASK
pin ^= INVERT_LIMIT_PIN_MASK; pin ^= INVERT_LIMIT_PIN_MASK;
#endif #endif
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { pin ^= LIMIT_MASK; } if (bit_isfalse(settings.flags, BITFLAG_INVERT_LIMIT_PINS)) {
if (pin) { pin ^= LIMIT_MASK;
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx)) { limit_state |= (1 << idx); }
} }
#ifdef ENABLE_DUAL_AXIS if (pin) {
if (pin & (1<<DUAL_LIMIT_BIT)) { limit_state |= (1 << N_AXIS); } uint8_t idx;
#endif for (idx = 0; idx < N_AXIS; idx++) {
} if (pin & get_limit_pin_mask(idx)) {
return(limit_state); limit_state |= (1 << idx);
}
}
#ifdef ENABLE_DUAL_AXIS
if (pin & (1 << DUAL_LIMIT_BIT)) {
limit_state |= (1 << N_AXIS);
}
#endif
}
return (limit_state);
} }
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing // This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls. // limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
// If a switch is triggered at all, something bad has happened and treat it as such, regardless // If a switch is triggered at all, something bad has happened and treat it as such, regardless
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a // if a limit switch is being disengaged. It's impossible to reliably tell the state of a
// bouncing pin because the Arduino microcontroller does not retain any state information when // bouncing pin because the Arduino microcontroller does not retain any state information when
// detecting a pin change. If we poll the pins in the ISR, you can miss the correct reading if the // detecting a pin change. If we poll the pins in the ISR, you can miss the correct reading if the
// switch is bouncing. // switch is bouncing.
// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during // NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
// homing cycles and will not respond correctly. Upon user request or need, there may be a // homing cycles and will not respond correctly. Upon user request or need, there may be a
// special pinout for an e-stop, but it is generally recommended to just directly connect // special pinout for an e-stop, but it is generally recommended to just directly connect
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this. // your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
#ifndef ENABLE_SOFTWARE_DEBOUNCE #ifndef ENABLE_SOFTWARE_DEBOUNCE
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process. ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{ {
// Ignore limit switches if already in an alarm state or in-process of executing an alarm. // Ignore limit switches if already in an alarm state or in-process of executing an alarm.
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending // When in the alarm state, Grbl should have been reset or will force a reset, so any pending
// moves in the planner and serial buffers are all cleared and newly sent blocks will be // moves in the planner and serial buffers are all cleared and newly sent blocks will be
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard // locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
// limit setting if their limits are constantly triggering after a reset and move their axes. // limit setting if their limits are constantly triggering after a reset and move their axes.
if (sys.state != STATE_ALARM) { if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) { if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK #ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state. // Check limit pin state.
if (limits_get_state()) { if (limits_get_state()) {
mc_reset(); // Initiate system kill. mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
} #endif
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (!(sys_rt_exec_alarm)) {
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
} }
}
} }
} }
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) {
if (!(WDTCSR & (1 << WDIE))) {
WDTCSR |= (1 << WDIE);
}
}
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1 << WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (!(sys_rt_exec_alarm)) {
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
}
}
}
#endif #endif
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after // Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
@ -154,277 +158,303 @@ uint8_t limits_get_state()
// circumvent the processes for executing motions in normal operation. // circumvent the processes for executing motions in normal operation.
// NOTE: Only the abort realtime command can interrupt this process. // NOTE: Only the abort realtime command can interrupt this process.
// TODO: Move limit pin-specific calls to a general function for portability. // TODO: Move limit pin-specific calls to a general function for portability.
void limits_go_home(uint8_t cycle_mask) void limits_go_home(uint8_t cycle_mask) {
{ if (sys.abort) {
if (sys.abort) { return; } // Block if system reset has been issued. return;
} // Block if system reset has been issued.
// Initialize plan data struct for homing motion. Spindle and coolant are disabled. // Initialize plan data struct for homing motion. Spindle and coolant are disabled.
plan_line_data_t plan_data; plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data; plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t)); memset(pl_data, 0, sizeof(plan_line_data_t));
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE); pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION | PL_COND_FLAG_NO_FEED_OVERRIDE);
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
pl_data->line_number = HOMING_CYCLE_LINE_NUMBER; pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
#endif #endif
// Initialize variables used for homing computations. // Initialize variables used for homing computations.
uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1); uint8_t n_cycle = (2 * N_HOMING_LOCATE_CYCLE + 1);
uint8_t step_pin[N_AXIS]; uint8_t step_pin[N_AXIS];
#ifdef ENABLE_DUAL_AXIS #ifdef ENABLE_DUAL_AXIS
uint8_t step_pin_dual; uint8_t step_pin_dual;
uint8_t dual_axis_async_check; uint8_t dual_axis_async_check;
int32_t dual_trigger_position; int32_t dual_trigger_position;
#if (DUAL_AXIS_SELECT == X_AXIS) #if (DUAL_AXIS_SELECT == X_AXIS)
float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[Y_AXIS]; float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT / 100.0) * settings.max_travel[Y_AXIS];
#else #else
float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT/100.0)*settings.max_travel[X_AXIS]; float fail_distance = (-DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT / 100.0) * settings.max_travel[X_AXIS];
#endif #endif
fail_distance = min(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX); fail_distance = min(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX);
fail_distance = max(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN); fail_distance = max(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN);
int32_t dual_fail_distance = trunc(fail_distance*settings.steps_per_mm[DUAL_AXIS_SELECT]); int32_t dual_fail_distance = trunc(fail_distance * settings.steps_per_mm[DUAL_AXIS_SELECT]);
// int32_t dual_fail_distance = trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]); // int32_t dual_fail_distance =
#endif // trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]);
float target[N_AXIS]; #endif
float max_travel = 0.0; float target[N_AXIS];
uint8_t idx; float max_travel = 0.0;
for (idx=0; idx<N_AXIS; idx++) { uint8_t idx;
// Initialize step pin masks for (idx = 0; idx < N_AXIS; idx++) {
step_pin[idx] = get_step_pin_mask(idx); // Initialize step pin masks
#ifdef COREXY step_pin[idx] = get_step_pin_mask(idx);
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); } #ifdef COREXY
#endif if ((idx == A_MOTOR) || (idx == B_MOTOR)) {
step_pin[idx] = (get_step_pin_mask(X_AXIS) | get_step_pin_mask(Y_AXIS));
if (bit_istrue(cycle_mask,bit(idx))) {
// Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
// NOTE: settings.max_travel[] is stored as a negative value.
max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]);
}
}
#ifdef ENABLE_DUAL_AXIS
step_pin_dual = (1<<DUAL_STEP_BIT);
#endif
// Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches.
bool approach = true;
float homing_rate = settings.homing_seek_rate;
uint8_t limit_state, axislock, n_active_axis;
do {
system_convert_array_steps_to_mpos(target,sys_position);
// Initialize and declare variables needed for homing routine.
axislock = 0;
#ifdef ENABLE_DUAL_AXIS
sys.homing_axis_lock_dual = 0;
dual_trigger_position = 0;
dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
#endif
n_active_axis = 0;
for (idx=0; idx<N_AXIS; idx++) {
// Set target location for active axes and setup computation for homing rate.
if (bit_istrue(cycle_mask,bit(idx))) {
n_active_axis++;
#ifdef COREXY
if (idx == X_AXIS) {
int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = axis_position;
sys_position[B_MOTOR] = -axis_position;
} else if (idx == Y_AXIS) {
int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position;
} else {
sys_position[Z_AXIS] = 0;
}
#else
sys_position[idx] = 0;
#endif
// Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (approach) { target[idx] = -max_travel; }
else { target[idx] = max_travel; }
} else {
if (approach) { target[idx] = max_travel; }
else { target[idx] = -max_travel; }
} }
// Apply axislock to the step port pins active in this cycle. #endif
axislock |= step_pin[idx];
#ifdef ENABLE_DUAL_AXIS
if (idx == DUAL_AXIS_SELECT) { sys.homing_axis_lock_dual = step_pin_dual; }
#endif
}
if (bit_istrue(cycle_mask, bit(idx))) {
// Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
// NOTE: settings.max_travel[] is stored as a negative value.
max_travel = max(max_travel, (-HOMING_AXIS_SEARCH_SCALAR) * settings.max_travel[idx]);
}
} }
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate. #ifdef ENABLE_DUAL_AXIS
sys.homing_axis_lock = axislock; step_pin_dual = (1 << DUAL_STEP_BIT);
#endif
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. // Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches.
pl_data->feed_rate = homing_rate; // Set current homing rate. bool approach = true;
plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion. float homing_rate = settings.homing_seek_rate;
sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags. uint8_t limit_state, axislock, n_active_axis;
st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do { do {
if (approach) {
// Check limit state. Lock out cycle axes when they change. system_convert_array_steps_to_mpos(target, sys_position);
limit_state = limits_get_state();
for (idx=0; idx<N_AXIS; idx++) { // Initialize and declare variables needed for homing routine.
if (axislock & step_pin[idx]) { axislock = 0;
if (limit_state & (1 << idx)) { #ifdef ENABLE_DUAL_AXIS
#ifdef COREXY sys.homing_axis_lock_dual = 0;
if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); } dual_trigger_position = 0;
else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); } dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
#else #endif
axislock &= ~(step_pin[idx]); n_active_axis = 0;
#ifdef ENABLE_DUAL_AXIS for (idx = 0; idx < N_AXIS; idx++) {
if (idx == DUAL_AXIS_SELECT) { dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_1; } // Set target location for active axes and setup computation for homing rate.
#endif if (bit_istrue(cycle_mask, bit(idx))) {
#endif n_active_axis++;
} #ifdef COREXY
} if (idx == X_AXIS) {
} int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys.homing_axis_lock = axislock; sys_position[A_MOTOR] = axis_position;
#ifdef ENABLE_DUAL_AXIS sys_position[B_MOTOR] = -axis_position;
if (sys.homing_axis_lock_dual) { // NOTE: Only true when homing dual axis. } else if (idx == Y_AXIS) {
if (limit_state & (1 << N_AXIS)) { int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys.homing_axis_lock_dual = 0; sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position;
dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_2; } else {
} sys_position[Z_AXIS] = 0;
}
// When first dual axis limit triggers, record position and begin checking distance until other limit triggers. Bail upon failure.
if (dual_axis_async_check) {
if (dual_axis_async_check & DUAL_AXIS_CHECK_ENABLE) {
if (( dual_axis_async_check & (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) == (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) {
dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
} else {
if (abs(dual_trigger_position - sys_position[DUAL_AXIS_SELECT]) > dual_fail_distance) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH);
mc_reset();
protocol_execute_realtime();
return;
} }
} #else
} else { sys_position[idx] = 0;
dual_axis_async_check |= DUAL_AXIS_CHECK_ENABLE; #endif
dual_trigger_position = sys_position[DUAL_AXIS_SELECT]; // Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
if (approach) {
target[idx] = -max_travel;
} else {
target[idx] = max_travel;
}
} else {
if (approach) {
target[idx] = max_travel;
} else {
target[idx] = -max_travel;
}
}
// Apply axislock to the step port pins active in this cycle.
axislock |= step_pin[idx];
#ifdef ENABLE_DUAL_AXIS
if (idx == DUAL_AXIS_SELECT) {
sys.homing_axis_lock_dual = step_pin_dual;
}
#endif
} }
}
#endif
}
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
// Exit routines: No time to run protocol_execute_realtime() in this loop.
if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
uint8_t rt_exec = sys_rt_exec_state;
// Homing failure condition: Reset issued during cycle.
if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
// Homing failure condition: Safety door was opened.
if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
// Homing failure condition: Limit switch still engaged after pull-off motion
if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); }
// Homing failure condition: Limit switch not found during approach.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); }
if (sys_rt_exec_alarm) {
mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime();
return;
} else {
// Pull-off motion complete. Disable CYCLE_STOP from executing.
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
break;
} }
} homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
sys.homing_axis_lock = axislock;
#ifdef ENABLE_DUAL_AXIS // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
} while ((STEP_MASK & axislock) || (sys.homing_axis_lock_dual)); pl_data->feed_rate = homing_rate; // Set current homing rate.
#else plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
} while (STEP_MASK & axislock);
#endif
st_reset(); // Immediately force kill steppers and reset step segment buffer. sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate. st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do {
if (approach) {
// Check limit state. Lock out cycle axes when they change.
limit_state = limits_get_state();
for (idx = 0; idx < N_AXIS; idx++) {
if (axislock & step_pin[idx]) {
if (limit_state & (1 << idx)) {
#ifdef COREXY
if (idx == Z_AXIS) {
axislock &= ~(step_pin[Z_AXIS]);
} else {
axislock &= ~(step_pin[A_MOTOR] | step_pin[B_MOTOR]);
}
#else
axislock &= ~(step_pin[idx]);
#ifdef ENABLE_DUAL_AXIS
if (idx == DUAL_AXIS_SELECT) {
dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_1;
}
#endif
#endif
}
}
}
sys.homing_axis_lock = axislock;
#ifdef ENABLE_DUAL_AXIS
if (sys.homing_axis_lock_dual) { // NOTE: Only true when homing dual axis.
if (limit_state & (1 << N_AXIS)) {
sys.homing_axis_lock_dual = 0;
dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_2;
}
}
// Reverse direction and reset homing rate for locate cycle(s). // When first dual axis limit triggers, record position and begin checking distance until other limit
approach = !approach; // triggers. Bail upon failure.
if (dual_axis_async_check) {
if (dual_axis_async_check & DUAL_AXIS_CHECK_ENABLE) {
if ((dual_axis_async_check & (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) ==
(DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) {
dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
} else {
if (abs(dual_trigger_position - sys_position[DUAL_AXIS_SELECT]) > dual_fail_distance) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH);
mc_reset();
protocol_execute_realtime();
return;
}
}
} else {
dual_axis_async_check |= DUAL_AXIS_CHECK_ENABLE;
dual_trigger_position = sys_position[DUAL_AXIS_SELECT];
}
}
#endif
}
// After first cycle, homing enters locating phase. Shorten search to pull-off distance. st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
if (approach) {
max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR; // Exit routines: No time to run protocol_execute_realtime() in this loop.
homing_rate = settings.homing_feed_rate; if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
} else { uint8_t rt_exec = sys_rt_exec_state;
max_travel = settings.homing_pulloff; // Homing failure condition: Reset issued during cycle.
homing_rate = settings.homing_seek_rate; if (rt_exec & EXEC_RESET) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET);
}
// Homing failure condition: Safety door was opened.
if (rt_exec & EXEC_SAFETY_DOOR) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR);
}
// Homing failure condition: Limit switch still engaged after pull-off motion
if (!approach && (limits_get_state() & cycle_mask)) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF);
}
// Homing failure condition: Limit switch not found during approach.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH);
}
if (sys_rt_exec_alarm) {
mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime();
return;
} else {
// Pull-off motion complete. Disable CYCLE_STOP from executing.
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
break;
}
}
#ifdef ENABLE_DUAL_AXIS
} while ((STEP_MASK & axislock) || (sys.homing_axis_lock_dual));
#else
} while (STEP_MASK & axislock);
#endif
st_reset(); // Immediately force kill steppers and reset step segment buffer.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
// Reverse direction and reset homing rate for locate cycle(s).
approach = !approach;
// After first cycle, homing enters locating phase. Shorten search to pull-off distance.
if (approach) {
max_travel = settings.homing_pulloff * HOMING_AXIS_LOCATE_SCALAR;
homing_rate = settings.homing_feed_rate;
} else {
max_travel = settings.homing_pulloff;
homing_rate = settings.homing_seek_rate;
}
} while (n_cycle-- > 0);
// The active cycle axes should now be homed and machine limits have been located. By
// default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches
// can be on either side of an axes, check and set axes machine zero appropriately. Also,
// set up pull-off maneuver from axes limit switches that have been homed. This provides
// some initial clearance off the switches and should also help prevent them from falsely
// triggering when hard limits are enabled or when more than one axes shares a limit pin.
int32_t set_axis_position;
// Set machine positions for homed limit switches. Don't update non-homed axes.
for (idx = 0; idx < N_AXIS; idx++) {
// NOTE: settings.max_travel[] is stored as a negative value.
if (cycle_mask & bit(idx)) {
#ifdef HOMING_FORCE_SET_ORIGIN
set_axis_position = 0;
#else
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
set_axis_position =
lround((settings.max_travel[idx] + settings.homing_pulloff) * settings.steps_per_mm[idx]);
} else {
set_axis_position = lround(-settings.homing_pulloff * settings.steps_per_mm[idx]);
}
#endif
#ifdef COREXY
if (idx == X_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = set_axis_position + off_axis_position;
sys_position[B_MOTOR] = set_axis_position - off_axis_position;
} else if (idx == Y_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = off_axis_position + set_axis_position;
sys_position[B_MOTOR] = off_axis_position - set_axis_position;
} else {
sys_position[idx] = set_axis_position;
}
#else
sys_position[idx] = set_axis_position;
#endif
}
} }
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
} while (n_cycle-- > 0);
// The active cycle axes should now be homed and machine limits have been located. By
// default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches
// can be on either side of an axes, check and set axes machine zero appropriately. Also,
// set up pull-off maneuver from axes limit switches that have been homed. This provides
// some initial clearance off the switches and should also help prevent them from falsely
// triggering when hard limits are enabled or when more than one axes shares a limit pin.
int32_t set_axis_position;
// Set machine positions for homed limit switches. Don't update non-homed axes.
for (idx=0; idx<N_AXIS; idx++) {
// NOTE: settings.max_travel[] is stored as a negative value.
if (cycle_mask & bit(idx)) {
#ifdef HOMING_FORCE_SET_ORIGIN
set_axis_position = 0;
#else
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
} else {
set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
}
#endif
#ifdef COREXY
if (idx==X_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = set_axis_position + off_axis_position;
sys_position[B_MOTOR] = set_axis_position - off_axis_position;
} else if (idx==Y_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = off_axis_position + set_axis_position;
sys_position[B_MOTOR] = off_axis_position - set_axis_position;
} else {
sys_position[idx] = set_axis_position;
}
#else
sys_position[idx] = set_axis_position;
#endif
}
}
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
} }
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed, // Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
// the workspace volume is in all negative space, and the system is in normal operation. // the workspace volume is in all negative space, and the system is in normal operation.
// NOTE: Used by jogging to limit travel within soft-limit volume. // NOTE: Used by jogging to limit travel within soft-limit volume.
void limits_soft_check(float *target) void limits_soft_check(float *target) {
{ if (system_check_travel_limits(target)) {
if (system_check_travel_limits(target)) { sys.soft_limit = true;
sys.soft_limit = true; // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
// Force feed hold if cycle is active. All buffered blocks are guaranteed to be within // workspace volume so just come to a controlled stop so position is not lost. When complete
// workspace volume so just come to a controlled stop so position is not lost. When complete // enter alarm mode.
// enter alarm mode. if (sys.state == STATE_CYCLE) {
if (sys.state == STATE_CYCLE) { system_set_exec_state_flag(EXEC_FEED_HOLD);
system_set_exec_state_flag(EXEC_FEED_HOLD); do {
do { protocol_execute_realtime();
protocol_execute_realtime(); if (sys.abort) {
if (sys.abort) { return; } return;
} while ( sys.state != STATE_IDLE ); }
} while (sys.state != STATE_IDLE);
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_SOFT_LIMIT); // Indicate soft limit critical event
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
return;
} }
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_SOFT_LIMIT); // Indicate soft limit critical event
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
return;
}
} }

View file

@ -22,7 +22,6 @@
#ifndef limits_h #ifndef limits_h
#define limits_h #define limits_h
// Initialize the limits module // Initialize the limits module
void limits_init(); void limits_init();

View file

@ -21,89 +21,89 @@
#include "grbl.h" #include "grbl.h"
// Declare system global variable structure // Declare system global variable structure
system_t sys; system_t sys;
int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps. int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps. int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR. volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks. volatile uint8_t
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides. volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides. volatile uint8_t
sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG #ifdef DEBUG
volatile uint8_t sys_rt_exec_debug; volatile uint8_t sys_rt_exec_debug;
#endif #endif
int main(void) {
// Initialize system upon power-up.
serial_init(); // Setup serial baud rate and interrupts
settings_init(); // Load Grbl settings from EEPROM
stepper_init(); // Configure stepper pins and interrupt timers
system_init(); // Configure pinout pins and pin-change interrupt
int main(void) memset(sys_position, 0, sizeof(sys_position)); // Clear machine position.
{ sei(); // Enable interrupts
// Initialize system upon power-up.
serial_init(); // Setup serial baud rate and interrupts
settings_init(); // Load Grbl settings from EEPROM
stepper_init(); // Configure stepper pins and interrupt timers
system_init(); // Configure pinout pins and pin-change interrupt
memset(sys_position,0,sizeof(sys_position)); // Clear machine position. // Initialize system state.
sei(); // Enable interrupts #ifdef FORCE_INITIALIZATION_ALARM
// Force Grbl into an ALARM state upon a power-cycle or hard reset.
// Initialize system state.
#ifdef FORCE_INITIALIZATION_ALARM
// Force Grbl into an ALARM state upon a power-cycle or hard reset.
sys.state = STATE_ALARM; sys.state = STATE_ALARM;
#else #else
sys.state = STATE_IDLE; sys.state = STATE_IDLE;
#endif #endif
// Check for power-up and set system alarm if homing is enabled to force homing cycle
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
// startup scripts, but allows access to settings and internal commands. Only a homing
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
// NOTE: The startup script will run after successful completion of the homing cycle, but
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
#endif
// Grbl initialization loop upon power-up or a system abort. For the latter, all processes // Check for power-up and set system alarm if homing is enabled to force homing cycle
// will return to this loop to be cleanly re-initialized. // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
for(;;) { // startup scripts, but allows access to settings and internal commands. Only a homing
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
// NOTE: The startup script will run after successful completion of the homing cycle, but
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)) {
sys.state = STATE_ALARM;
}
#endif
// Reset system variables. // Grbl initialization loop upon power-up or a system abort. For the latter, all processes
uint8_t prior_state = sys.state; // will return to this loop to be cleanly re-initialized.
memset(&sys, 0, sizeof(system_t)); // Clear system struct variable. for (;;) {
sys.state = prior_state;
sys.f_override = DEFAULT_FEED_OVERRIDE; // Set to 100%
sys.r_override = DEFAULT_RAPID_OVERRIDE; // Set to 100%
sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE; // Set to 100%
memset(sys_probe_position,0,sizeof(sys_probe_position)); // Clear probe position.
sys_probe_state = 0;
sys_rt_exec_state = 0;
sys_rt_exec_alarm = 0;
sys_rt_exec_motion_override = 0;
sys_rt_exec_accessory_override = 0;
// Reset Grbl primary systems. // Reset system variables.
serial_reset_read_buffer(); // Clear serial read buffer uint8_t prior_state = sys.state;
gc_init(); // Set g-code parser to default state memset(&sys, 0, sizeof(system_t)); // Clear system struct variable.
spindle_init(); sys.state = prior_state;
coolant_init(); sys.f_override = DEFAULT_FEED_OVERRIDE; // Set to 100%
limits_init(); sys.r_override = DEFAULT_RAPID_OVERRIDE; // Set to 100%
probe_init(); sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE; // Set to 100%
plan_reset(); // Clear block buffer and planner variables memset(sys_probe_position, 0, sizeof(sys_probe_position)); // Clear probe position.
st_reset(); // Clear stepper subsystem variables. sys_probe_state = 0;
sys_rt_exec_state = 0;
sys_rt_exec_alarm = 0;
sys_rt_exec_motion_override = 0;
sys_rt_exec_accessory_override = 0;
// Sync cleared gcode and planner positions to current system position. // Reset Grbl primary systems.
plan_sync_position(); serial_reset_read_buffer(); // Clear serial read buffer
gc_sync_position(); gc_init(); // Set g-code parser to default state
spindle_init();
coolant_init();
limits_init();
probe_init();
plan_reset(); // Clear block buffer and planner variables
st_reset(); // Clear stepper subsystem variables.
// Print welcome message. Indicates an initialization has occured at power-up or with a reset. // Sync cleared gcode and planner positions to current system position.
report_init_message(); plan_sync_position();
gc_sync_position();
// Start Grbl main loop. Processes program inputs and executes them. // Print welcome message. Indicates an initialization has occured at power-up or with a reset.
protocol_main_loop(); report_init_message();
} // Start Grbl main loop. Processes program inputs and executes them.
return 0; /* Never reached */ protocol_main_loop();
}
return 0; /* Never reached */
} }

View file

@ -21,7 +21,6 @@
#include "grbl.h" #include "grbl.h"
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time. // (1 minute)/feed_rate time.
@ -29,53 +28,61 @@
// segments, must pass through this routine before being passed to the planner. The seperation of // segments, must pass through this routine before being passed to the planner. The seperation of
// mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being // mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being
// in the planner and to let backlash compensation or canned cycle integration simple and direct. // in the planner and to let backlash compensation or canned cycle integration simple and direct.
void mc_line(float *target, plan_line_data_t *pl_data) void mc_line(float *target, plan_line_data_t *pl_data) {
{ // If enabled, check for soft limit violations. Placed here all line motions are picked up
// If enabled, check for soft limit violations. Placed here all line motions are picked up // from everywhere in Grbl.
// from everywhere in Grbl. if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) {
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { // NOTE: Block jog state. Jogging is a special case and soft limits are handled independently.
// NOTE: Block jog state. Jogging is a special case and soft limits are handled independently. if (sys.state != STATE_JOG) {
if (sys.state != STATE_JOG) { limits_soft_check(target); } limits_soft_check(target);
} }
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) { return; }
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
// to insert a backlash line motion(s) before the intended line motion and will require its own
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
// backlash steps will need to be also tracked, which will need to be kept at a system level.
// There are likely some other things that will need to be tracked as well. However, we feel
// that backlash compensation should NOT be handled by Grbl itself, because there are a myriad
// of ways to implement it and can be effective or ineffective for different CNC machines. This
// would be better handled by the interface as a post-processor task, where the original g-code
// is translated and inserts backlash motions that best suits the machine.
// NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that
// indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but
// doesn't update the machine position values. Since the position values used by the g-code
// parser and planner are separate from the system machine positions, this is doable.
// If the buffer is full: good! That means we are well ahead of the robot.
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_realtime(); // Check for any run-time commands
if (sys.abort) { return; } // Bail, if system abort.
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
else { break; }
} while (1);
// Plan and queue motion into planner buffer
if (plan_buffer_line(target, pl_data) == PLAN_EMPTY_BLOCK) {
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
// Correctly set spindle state, if there is a coincident position passed. Forces a buffer
// sync while in M3 laser mode only.
if (pl_data->condition & PL_COND_FLAG_SPINDLE_CW) {
spindle_sync(PL_COND_FLAG_SPINDLE_CW, pl_data->spindle_speed);
}
} }
}
}
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) {
return;
}
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
// to insert a backlash line motion(s) before the intended line motion and will require its own
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
// backlash steps will need to be also tracked, which will need to be kept at a system level.
// There are likely some other things that will need to be tracked as well. However, we feel
// that backlash compensation should NOT be handled by Grbl itself, because there are a myriad
// of ways to implement it and can be effective or ineffective for different CNC machines. This
// would be better handled by the interface as a post-processor task, where the original g-code
// is translated and inserts backlash motions that best suits the machine.
// NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that
// indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but
// doesn't update the machine position values. Since the position values used by the g-code
// parser and planner are separate from the system machine positions, this is doable.
// If the buffer is full: good! That means we are well ahead of the robot.
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_realtime(); // Check for any run-time commands
if (sys.abort) {
return;
} // Bail, if system abort.
if (plan_check_full_buffer()) {
protocol_auto_cycle_start();
} // Auto-cycle start when buffer is full.
else {
break;
}
} while (1);
// Plan and queue motion into planner buffer
if (plan_buffer_line(target, pl_data) == PLAN_EMPTY_BLOCK) {
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) {
// Correctly set spindle state, if there is a coincident position passed. Forces a buffer
// sync while in M3 laser mode only.
if (pl_data->condition & PL_COND_FLAG_SPINDLE_CW) {
spindle_sync(PL_COND_FLAG_SPINDLE_CW, pl_data->spindle_speed);
}
}
}
}
// Execute an arc in offset mode format. position == current xyz, target == target xyz, // Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is // offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is
@ -84,305 +91,327 @@ void mc_line(float *target, plan_line_data_t *pl_data)
// The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal
// distance from segment to the circle when the end points both lie on the circle. // distance from segment to the circle when the end points both lie on the circle.
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, uint8_t axis_0,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) {
{ float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis0 = position[axis_0] + offset[axis_0]; float center_axis1 = position[axis_1] + offset[axis_1];
float center_axis1 = position[axis_1] + offset[axis_1]; float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location float r_axis1 = -offset[axis_1];
float r_axis1 = -offset[axis_1]; float rt_axis0 = target[axis_0] - center_axis0;
float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1;
float rt_axis1 = target[axis_1] - center_axis1;
// CCW angle between position and target from circle center. Only one atan2() trig computation required. // CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); float angular_travel = atan2(r_axis0 * rt_axis1 - r_axis1 * rt_axis0, r_axis0 * rt_axis0 + r_axis1 * rt_axis1);
if (is_clockwise_arc) { // Correct atan2 output per direction if (is_clockwise_arc) { // Correct atan2 output per direction
if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= 2*M_PI; } if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) {
} else { angular_travel -= 2 * M_PI;
if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += 2*M_PI; } }
} } else {
if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) {
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to angular_travel += 2 * M_PI;
// (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit }
// is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
// For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
uint16_t segments = floor(fabs(0.5*angular_travel*radius)/
sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
if (segments) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
// all segments.
if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) {
pl_data->feed_rate *= segments;
bit_false(pl_data->condition,PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments.
} }
float theta_per_segment = angular_travel/segments;
float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
and phi is the angle of rotation. Solution approach by Jens Geisler. // (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit
r_T = [cos(phi) -sin(phi); // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
sin(phi) cos(phi] * r ; // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
uint16_t segments = floor(fabs(0.5 * angular_travel * radius) /
sqrt(settings.arc_tolerance * (2 * radius - settings.arc_tolerance)));
For arc generation, the center of the circle is the axis of rotation and the radius vector is if (segments) {
defined from the circle center to the initial position. Each line segment is formed by successive // Multiply inverse feed_rate to compensate for the fact that this movement is approximated
vector rotations. Single precision values can accumulate error greater than tool precision in rare // by a number of discrete segments. The inverse feed_rate should be correct for the sum of
cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very // all segments.
expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute. if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) {
pl_data->feed_rate *= segments;
bit_false(pl_data->condition, PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments.
}
Small angle approximation may be used to reduce computation overhead further. A third-order approximation float theta_per_segment = angular_travel / segments;
(second order sin() has too much error) holds for most, if not, all CNC applications. Note that this float linear_per_segment = (target[axis_linear] - position[axis_linear]) / segments;
approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than
~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This
scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated
and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC
applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a
low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate.
This approximation also allows mc_arc to immediately insert a line segment into the planner /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied and phi is the angle of rotation. Solution approach by Jens Geisler.
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead. r_T = [cos(phi) -sin(phi);
This is important when there are successive arc motions. sin(phi) cos(phi] * r ;
*/
// Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec
float cos_T = 2.0 - theta_per_segment*theta_per_segment;
float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0);
cos_T *= 0.5;
float sin_Ti; For arc generation, the center of the circle is the axis of rotation and the radius vector is
float cos_Ti; defined from the circle center to the initial position. Each line segment is formed by successive
float r_axisi; vector rotations. Single precision values can accumulate error greater than tool precision in rare
uint16_t i; cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very
uint8_t count = 0; expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute.
for (i = 1; i<segments; i++) { // Increment (segments-1). Small angle approximation may be used to reduce computation overhead further. A third-order approximation
(second order sin() has too much error) holds for most, if not, all CNC applications. Note that this
approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than
~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This
scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated
and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC
applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a
low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate.
if (count < N_ARC_CORRECTION) { This approximation also allows mc_arc to immediately insert a line segment into the planner
// Apply vector rotation matrix. ~40 usec without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
r_axisi = r_axis0*sin_T + r_axis1*cos_T; a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
r_axis0 = r_axis0*cos_T - r_axis1*sin_T; This is important when there are successive arc motions.
r_axis1 = r_axisi; */
count++; // Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec
} else { float cos_T = 2.0 - theta_per_segment * theta_per_segment;
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. ~375 usec float sin_T = theta_per_segment * 0.16666667 * (cos_T + 4.0);
// Compute exact location by applying transformation matrix from initial radius vector(=-offset). cos_T *= 0.5;
cos_Ti = cos(i*theta_per_segment);
sin_Ti = sin(i*theta_per_segment);
r_axis0 = -offset[axis_0]*cos_Ti + offset[axis_1]*sin_Ti;
r_axis1 = -offset[axis_0]*sin_Ti - offset[axis_1]*cos_Ti;
count = 0;
}
// Update arc_target location float sin_Ti;
position[axis_0] = center_axis0 + r_axis0; float cos_Ti;
position[axis_1] = center_axis1 + r_axis1; float r_axisi;
position[axis_linear] += linear_per_segment; uint16_t i;
uint8_t count = 0;
mc_line(position, pl_data); for (i = 1; i < segments; i++) { // Increment (segments-1).
// Bail mid-circle on system abort. Runtime command check already performed by mc_line. if (count < N_ARC_CORRECTION) {
if (sys.abort) { return; } // Apply vector rotation matrix. ~40 usec
r_axisi = r_axis0 * sin_T + r_axis1 * cos_T;
r_axis0 = r_axis0 * cos_T - r_axis1 * sin_T;
r_axis1 = r_axisi;
count++;
} else {
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. ~375 usec
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti = cos(i * theta_per_segment);
sin_Ti = sin(i * theta_per_segment);
r_axis0 = -offset[axis_0] * cos_Ti + offset[axis_1] * sin_Ti;
r_axis1 = -offset[axis_0] * sin_Ti - offset[axis_1] * cos_Ti;
count = 0;
}
// Update arc_target location
position[axis_0] = center_axis0 + r_axis0;
position[axis_1] = center_axis1 + r_axis1;
position[axis_linear] += linear_per_segment;
mc_line(position, pl_data);
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys.abort) {
return;
}
}
} }
} // Ensure last segment arrives at target location.
// Ensure last segment arrives at target location. mc_line(target, pl_data);
mc_line(target, pl_data);
} }
// Execute dwell in seconds. // Execute dwell in seconds.
void mc_dwell(float seconds) void mc_dwell(float seconds) {
{ if (sys.state == STATE_CHECK_MODE) {
if (sys.state == STATE_CHECK_MODE) { return; } return;
protocol_buffer_synchronize(); }
delay_sec(seconds, DELAY_MODE_DWELL); protocol_buffer_synchronize();
delay_sec(seconds, DELAY_MODE_DWELL);
} }
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command. // Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
// executing the homing cycle. This prevents incorrect buffered plans after homing. // executing the homing cycle. This prevents incorrect buffered plans after homing.
void mc_homing_cycle(uint8_t cycle_mask) void mc_homing_cycle(uint8_t cycle_mask) {
{ // Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems
// Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems // with machines with limits wired on both ends of travel to one limit pin.
// with machines with limits wired on both ends of travel to one limit pin. // TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function.
// TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function. #ifdef LIMITS_TWO_SWITCHES_ON_AXES
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
if (limits_get_state()) { if (limits_get_state()) {
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT);
return; return;
} }
#endif #endif
limits_disable(); // Disable hard limits pin change register for cycle duration limits_disable(); // Disable hard limits pin change register for cycle duration
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
// Perform homing routine. NOTE: Special motion case. Only system reset works. // Perform homing routine. NOTE: Special motion case. Only system reset works.
#ifdef HOMING_SINGLE_AXIS_COMMANDS #ifdef HOMING_SINGLE_AXIS_COMMANDS
if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask. if (cycle_mask) {
limits_go_home(cycle_mask);
} // Perform homing cycle based on mask.
else else
#endif #endif
{ {
// Search to engage all axes limit switches at faster homing seek rate. // Search to engage all axes limit switches at faster homing seek rate.
limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 limits_go_home(HOMING_CYCLE_0); // Homing cycle 0
#ifdef HOMING_CYCLE_1 #ifdef HOMING_CYCLE_1
limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 limits_go_home(HOMING_CYCLE_1); // Homing cycle 1
#endif #endif
#ifdef HOMING_CYCLE_2 #ifdef HOMING_CYCLE_2
limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 limits_go_home(HOMING_CYCLE_2); // Homing cycle 2
#endif #endif
} }
protocol_execute_realtime(); // Check for reset and set system abort. protocol_execute_realtime(); // Check for reset and set system abort.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. if (sys.abort) {
return;
} // Did not complete. Alarm state set by mc_alarm.
// Homing cycle complete! Setup system for normal operation. // Homing cycle complete! Setup system for normal operation.
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
// Sync gcode parser and planner positions to homed position. // Sync gcode parser and planner positions to homed position.
gc_sync_position(); gc_sync_position();
plan_sync_position(); plan_sync_position();
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
limits_init(); limits_init();
} }
// Perform tool length probe cycle. Requires probe switch. // Perform tool length probe cycle. Requires probe switch.
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state. // NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags) uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags) {
{ // TODO: Need to update this cycle so it obeys a non-auto cycle start.
// TODO: Need to update this cycle so it obeys a non-auto cycle start. if (sys.state == STATE_CHECK_MODE) {
if (sys.state == STATE_CHECK_MODE) { return(GC_PROBE_CHECK_MODE); } return (GC_PROBE_CHECK_MODE);
}
// Finish all queued commands and empty planner buffer before starting probe cycle. // Finish all queued commands and empty planner buffer before starting probe cycle.
protocol_buffer_synchronize(); protocol_buffer_synchronize();
if (sys.abort) { return(GC_PROBE_ABORT); } // Return if system reset has been issued. if (sys.abort) {
return (GC_PROBE_ABORT);
} // Return if system reset has been issued.
// Initialize probing control variables // Initialize probing control variables
uint8_t is_probe_away = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_AWAY); uint8_t is_probe_away = bit_istrue(parser_flags, GC_PARSER_PROBE_IS_AWAY);
uint8_t is_no_error = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_NO_ERROR); uint8_t is_no_error = bit_istrue(parser_flags, GC_PARSER_PROBE_IS_NO_ERROR);
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle. sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
probe_configure_invert_mask(is_probe_away); probe_configure_invert_mask(is_probe_away);
// After syncing, check if probe is already triggered. If so, halt and issue alarm. // After syncing, check if probe is already triggered. If so, halt and issue alarm.
// NOTE: This probe initialization error applies to all probing cycles. // NOTE: This probe initialization error applies to all probing cycles.
if ( probe_get_state() ) { // Check probe pin state. if (probe_get_state()) { // Check probe pin state.
system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_INITIAL); system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_INITIAL);
protocol_execute_realtime(); protocol_execute_realtime();
probe_configure_invert_mask(false); // Re-initialize invert mask before returning. probe_configure_invert_mask(false); // Re-initialize invert mask before returning.
return(GC_PROBE_FAIL_INIT); // Nothing else to do but bail. return (GC_PROBE_FAIL_INIT); // Nothing else to do but bail.
} }
// Setup and queue probing motion. Auto cycle-start should not start the cycle. // Setup and queue probing motion. Auto cycle-start should not start the cycle.
mc_line(target, pl_data); mc_line(target, pl_data);
// Activate the probing state monitor in the stepper module. // Activate the probing state monitor in the stepper module.
sys_probe_state = PROBE_ACTIVE; sys_probe_state = PROBE_ACTIVE;
// Perform probing cycle. Wait here until probe is triggered or motion completes. // Perform probing cycle. Wait here until probe is triggered or motion completes.
system_set_exec_state_flag(EXEC_CYCLE_START); system_set_exec_state_flag(EXEC_CYCLE_START);
do { do {
protocol_execute_realtime(); protocol_execute_realtime();
if (sys.abort) { return(GC_PROBE_ABORT); } // Check for system abort if (sys.abort) {
} while (sys.state != STATE_IDLE); return (GC_PROBE_ABORT);
} // Check for system abort
} while (sys.state != STATE_IDLE);
// Probing cycle complete! // Probing cycle complete!
// Set state variables and error out, if the probe failed and cycle with error is enabled. // Set state variables and error out, if the probe failed and cycle with error is enabled.
if (sys_probe_state == PROBE_ACTIVE) { if (sys_probe_state == PROBE_ACTIVE) {
if (is_no_error) { memcpy(sys_probe_position, sys_position, sizeof(sys_position)); } if (is_no_error) {
else { system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT); } memcpy(sys_probe_position, sys_position, sizeof(sys_position));
} else { } else {
sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully. system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT);
} }
sys_probe_state = PROBE_OFF; // Ensure probe state monitor is disabled. } else {
probe_configure_invert_mask(false); // Re-initialize invert mask. sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully.
protocol_execute_realtime(); // Check and execute run-time commands }
sys_probe_state = PROBE_OFF; // Ensure probe state monitor is disabled.
probe_configure_invert_mask(false); // Re-initialize invert mask.
protocol_execute_realtime(); // Check and execute run-time commands
// Reset the stepper and planner buffers to remove the remainder of the probe motion. // Reset the stepper and planner buffers to remove the remainder of the probe motion.
st_reset(); // Reset step segment buffer. st_reset(); // Reset step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position. plan_sync_position(); // Sync planner position to current machine position.
#ifdef MESSAGE_PROBE_COORDINATES #ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message. // All done! Output the probe position as message.
report_probe_parameters(); report_probe_parameters();
#endif #endif
if (sys.probe_succeeded) { return(GC_PROBE_FOUND); } // Successful probe cycle. if (sys.probe_succeeded) {
else { return(GC_PROBE_FAIL_END); } // Failed to trigger probe within travel. With or without error. return (GC_PROBE_FOUND);
} // Successful probe cycle.
else {
return (GC_PROBE_FAIL_END);
} // Failed to trigger probe within travel. With or without error.
} }
// Plans and executes the single special motion case for parking. Independent of main planner buffer. // 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. // NOTE: Uses the always free planner ring buffer head to store motion parameters for execution.
#ifdef PARKING_ENABLE #ifdef PARKING_ENABLE
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data) void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data) {
{ if (sys.abort) {
if (sys.abort) { return; } // Block during abort. return;
} // Block during abort.
uint8_t plan_status = plan_buffer_line(parking_target, pl_data); uint8_t plan_status = plan_buffer_line(parking_target, pl_data);
if (plan_status) { if (plan_status) {
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION); bit_true(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
bit_false(sys.step_control, STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active. bit_false(sys.step_control,
st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
st_prep_buffer(); st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case
st_wake_up(); st_prep_buffer();
do { st_wake_up();
protocol_exec_rt_system(); do {
if (sys.abort) { return; } protocol_exec_rt_system();
} while (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION); if (sys.abort) {
st_parking_restore_buffer(); // Restore step segment buffer to normal run state. return;
}
} while (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION);
st_parking_restore_buffer(); // Restore step segment buffer to normal run state.
} else { } else {
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION); bit_false(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
protocol_exec_rt_system(); protocol_exec_rt_system();
} }
}
}
#endif #endif
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
void mc_override_ctrl_update(uint8_t override_state) void mc_override_ctrl_update(uint8_t override_state) {
{
// Finish all queued commands before altering override control state // Finish all queued commands before altering override control state
protocol_buffer_synchronize(); protocol_buffer_synchronize();
if (sys.abort) { return; } if (sys.abort) {
return;
}
sys.override_ctrl = override_state; sys.override_ctrl = override_state;
} }
#endif #endif
// 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
// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by // lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by
// realtime abort command and hard limits. So, keep to a minimum. // realtime abort command and hard limits. So, keep to a minimum.
void mc_reset() void mc_reset() {
{ // Only this function can set the system reset. Helps prevent multiple kill calls.
// Only this function can set the system reset. Helps prevent multiple kill calls. if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) {
if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) { system_set_exec_state_flag(EXEC_RESET);
system_set_exec_state_flag(EXEC_RESET);
// Kill spindle and coolant. // Kill spindle and coolant.
spindle_stop(); spindle_stop();
coolant_stop(); coolant_stop();
// Kill steppers only if in any motion state, i.e. cycle, actively holding, or homing. // Kill steppers only if in any motion state, i.e. cycle, actively holding, or homing.
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps // 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 | STATE_JOG)) || if ((sys.state & (STATE_CYCLE | STATE_HOMING | STATE_JOG)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) { (sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) {
if (sys.state == STATE_HOMING) { if (sys.state == STATE_HOMING) {
if (!sys_rt_exec_alarm) {system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); } if (!sys_rt_exec_alarm) {
} else { system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); } system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET);
st_go_idle(); // Force kill steppers. Position has likely been lost. }
} else {
system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE);
}
st_go_idle(); // Force kill steppers. Position has likely been lost.
}
} }
}
} }

View file

@ -22,16 +22,14 @@
#ifndef motion_control_h #ifndef motion_control_h
#define motion_control_h #define motion_control_h
// System motion commands must have a line number of zero. // System motion commands must have a line number of zero.
#define HOMING_CYCLE_LINE_NUMBER 0 #define HOMING_CYCLE_LINE_NUMBER 0
#define PARKING_MOTION_LINE_NUMBER 0 #define PARKING_MOTION_LINE_NUMBER 0
#define HOMING_CYCLE_ALL 0 // Must be zero. #define HOMING_CYCLE_ALL 0 // Must be zero.
#define HOMING_CYCLE_X bit(X_AXIS) #define HOMING_CYCLE_X bit(X_AXIS)
#define HOMING_CYCLE_Y bit(Y_AXIS) #define HOMING_CYCLE_Y bit(Y_AXIS)
#define HOMING_CYCLE_Z bit(Z_AXIS) #define HOMING_CYCLE_Z bit(Z_AXIS)
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
@ -42,8 +40,8 @@ void mc_line(float *target, plan_line_data_t *pl_data);
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used // the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used
// for vector transformation direction. // for vector transformation direction.
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, uint8_t axis_0,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc); uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc);
// Dwell for a specific number of seconds // Dwell for a specific number of seconds
void mc_dwell(float seconds); void mc_dwell(float seconds);

View file

@ -21,10 +21,8 @@
#include "grbl.h" #include "grbl.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
// Extracts a floating point value from a string. The following code is based loosely on // Extracts a floating point value from a string. The following code is based loosely on
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely // the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
// available conversion method examples, but has been highly optimized for Grbl. For known // available conversion method examples, but has been highly optimized for Grbl. For known
@ -32,159 +30,163 @@
// Scientific notation is officially not supported by g-code, and the 'E' character may // Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized. // be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). // NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) {
{ char *ptr = line + *char_counter;
char *ptr = line + *char_counter; unsigned char c;
unsigned char c;
// Grab first character and increment pointer. No spaces assumed in line. // Grab first character and increment pointer. No spaces assumed in line.
c = *ptr++;
// Capture initial positive/minus character
bool isnegative = false;
if (c == '-') {
isnegative = true;
c = *ptr++; c = *ptr++;
} else if (c == '+') {
c = *ptr++;
}
// Extract number into fast integer. Track decimal in terms of exponent value. // Capture initial positive/minus character
uint32_t intval = 0; bool isnegative = false;
int8_t exp = 0; if (c == '-') {
uint8_t ndigit = 0; isnegative = true;
bool isdecimal = false; c = *ptr++;
while(1) { } else if (c == '+') {
c -= '0'; c = *ptr++;
if (c <= 9) { }
ndigit++;
if (ndigit <= MAX_INT_DIGITS) { // Extract number into fast integer. Track decimal in terms of exponent value.
if (isdecimal) { exp--; } uint32_t intval = 0;
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c int8_t exp = 0;
} else { uint8_t ndigit = 0;
if (!(isdecimal)) { exp++; } // Drop overflow digits bool isdecimal = false;
} while (1) {
} else if (c == (('.'-'0') & 0xff) && !(isdecimal)) { c -= '0';
isdecimal = true; if (c <= 9) {
ndigit++;
if (ndigit <= MAX_INT_DIGITS) {
if (isdecimal) {
exp--;
}
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
} else {
if (!(isdecimal)) {
exp++;
} // Drop overflow digits
}
} else if (c == (('.' - '0') & 0xff) && !(isdecimal)) {
isdecimal = true;
} else {
break;
}
c = *ptr++;
}
// Return if no digits have been read.
if (!ndigit) {
return (false);
};
// Convert integer into floating point.
float fval;
fval = (float)intval;
// Apply decimal. Should perform no more than two floating point multiplications for the
// expected range of E0 to E-4.
if (fval != 0) {
while (exp <= -2) {
fval *= 0.01;
exp += 2;
}
if (exp < 0) {
fval *= 0.1;
} else if (exp > 0) {
do {
fval *= 10.0;
} while (--exp > 0);
}
}
// Assign floating point value with correct sign.
if (isnegative) {
*float_ptr = -fval;
} else { } else {
break; *float_ptr = fval;
} }
c = *ptr++;
}
// Return if no digits have been read. *char_counter = ptr - line - 1; // Set char_counter to next statement
if (!ndigit) { return(false); };
// Convert integer into floating point. return (true);
float fval;
fval = (float)intval;
// Apply decimal. Should perform no more than two floating point multiplications for the
// expected range of E0 to E-4.
if (fval != 0) {
while (exp <= -2) {
fval *= 0.01;
exp += 2;
}
if (exp < 0) {
fval *= 0.1;
} else if (exp > 0) {
do {
fval *= 10.0;
} while (--exp > 0);
}
}
// Assign floating point value with correct sign.
if (isnegative) {
*float_ptr = -fval;
} else {
*float_ptr = fval;
}
*char_counter = ptr - line - 1; // Set char_counter to next statement
return(true);
} }
// Non-blocking delay function used for general operation and suspend features. // Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode) void delay_sec(float seconds, uint8_t mode) {
{ uint16_t i = ceil(1000 / DWELL_TIME_STEP * seconds);
uint16_t i = ceil(1000/DWELL_TIME_STEP*seconds); while (i-- > 0) {
while (i-- > 0) { if (sys.abort) {
if (sys.abort) { return; } return;
if (mode == DELAY_MODE_DWELL) { }
protocol_execute_realtime(); if (mode == DELAY_MODE_DWELL) {
} else { // DELAY_MODE_SYS_SUSPEND protocol_execute_realtime();
// Execute rt_system() only to avoid nesting suspend loops. } else { // DELAY_MODE_SYS_SUSPEND
protocol_exec_rt_system(); // Execute rt_system() only to avoid nesting suspend loops.
if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens. protocol_exec_rt_system();
} if (sys.suspend & SUSPEND_RESTART_RETRACT) {
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment 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) {
{ while (ms--) {
while ( ms-- ) { _delay_ms(1); } _delay_ms(1);
}
} }
// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(),
// which only accepts constants in future compiler releases. Written to perform more // which only accepts constants in future compiler releases. Written to perform more
// efficiently with larger delays, as the counter adds parasitic time in each iteration. // efficiently with larger delays, as the counter adds parasitic time in each iteration.
void delay_us(uint32_t us) void delay_us(uint32_t us) {
{ while (us) {
while (us) { if (us < 10) {
if (us < 10) { _delay_us(1);
_delay_us(1); us--;
us--; } else if (us < 100) {
} else if (us < 100) { _delay_us(10);
_delay_us(10); us -= 10;
us -= 10; } else if (us < 1000) {
} else if (us < 1000) { _delay_us(100);
_delay_us(100); us -= 100;
us -= 100; } else {
} else { _delay_ms(1);
_delay_ms(1); us -= 1000;
us -= 1000; }
} }
}
} }
// Simple hypotenuse computation function. // Simple hypotenuse computation function.
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); } float hypot_f(float x, float y) {
return (sqrt(x * x + y * y));
float convert_delta_vector_to_unit_vector(float *vector)
{
uint8_t idx;
float magnitude = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
if (vector[idx] != 0.0) {
magnitude += vector[idx]*vector[idx];
}
}
magnitude = sqrt(magnitude);
float inv_magnitude = 1.0/magnitude;
for (idx=0; idx<N_AXIS; idx++) { vector[idx] *= inv_magnitude; }
return(magnitude);
} }
float convert_delta_vector_to_unit_vector(float *vector) {
float limit_value_by_axis_maximum(float *max_value, float *unit_vec) uint8_t idx;
{ float magnitude = 0.0;
uint8_t idx; for (idx = 0; idx < N_AXIS; idx++) {
float limit_value = SOME_LARGE_VALUE; if (vector[idx] != 0.0) {
for (idx=0; idx<N_AXIS; idx++) { magnitude += vector[idx] * vector[idx];
if (unit_vec[idx] != 0) { // Avoid divide by zero. }
limit_value = min(limit_value,fabs(max_value[idx]/unit_vec[idx]));
} }
} magnitude = sqrt(magnitude);
return(limit_value); float inv_magnitude = 1.0 / magnitude;
for (idx = 0; idx < N_AXIS; idx++) {
vector[idx] *= inv_magnitude;
}
return (magnitude);
}
float limit_value_by_axis_maximum(float *max_value, float *unit_vec) {
uint8_t idx;
float limit_value = SOME_LARGE_VALUE;
for (idx = 0; idx < N_AXIS; idx++) {
if (unit_vec[idx] != 0) { // Avoid divide by zero.
limit_value = min(limit_value, fabs(max_value[idx] / unit_vec[idx]));
}
}
return (limit_value);
} }

View file

@ -23,7 +23,7 @@
#define nuts_bolts_h #define nuts_bolts_h
#define false 0 #define false 0
#define true 1 #define true 1
#define SOME_LARGE_VALUE 1.0E+38 #define SOME_LARGE_VALUE 1.0E+38
@ -37,32 +37,32 @@
// CoreXY motor assignments. DO NOT ALTER. // CoreXY motor assignments. DO NOT ALTER.
// NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations.
#ifdef COREXY #ifdef COREXY
#define A_MOTOR X_AXIS // Must be X_AXIS #define A_MOTOR X_AXIS // Must be X_AXIS
#define B_MOTOR Y_AXIS // Must be Y_AXIS #define B_MOTOR Y_AXIS // Must be Y_AXIS
#endif #endif
// Conversions // Conversions
#define MM_PER_INCH (25.40) #define MM_PER_INCH (25.40)
#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_DWELL 0
#define DELAY_MODE_SYS_SUSPEND 1 #define DELAY_MODE_SYS_SUSPEND 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)
// #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS) // #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS)
#define max(a,b) (((a) > (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b)) #define min(a, b) (((a) < (b)) ? (a) : (b))
#define isequal_position_vector(a,b) !(memcmp(a, b, sizeof(float)*N_AXIS)) #define isequal_position_vector(a, b) !(memcmp(a, b, sizeof(float) * N_AXIS))
// Bit field and masking macros // Bit field and masking macros
#define bit(n) (1 << n) #define bit(n) (1 << n)
#define bit_true(x,mask) (x) |= (mask) #define bit_true(x, mask) (x) |= (mask)
#define bit_false(x,mask) (x) &= ~(mask) #define bit_false(x, mask) (x) &= ~(mask)
#define bit_istrue(x,mask) ((x & mask) != 0) #define bit_istrue(x, mask) ((x & mask) != 0)
#define bit_isfalse(x,mask) ((x & mask) == 0) #define bit_isfalse(x, mask) ((x & mask) == 0)
// Read a floating point value from a string. Line points to the input buffer, char_counter // Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is // is the indexer pointing to the current character of the line, while float_ptr is

View file

@ -22,42 +22,41 @@
#include "grbl.h" #include "grbl.h"
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions static uint8_t block_buffer_tail; // Index of the block to process now
static uint8_t block_buffer_tail; // Index of the block to process now static uint8_t block_buffer_head; // Index of the next block to be pushed
static uint8_t block_buffer_head; // Index of the next block to be pushed static uint8_t next_buffer_head; // Index of the next buffer head
static uint8_t next_buffer_head; // Index of the next buffer head static uint8_t block_buffer_planned; // Index of the optimally planned block
static uint8_t block_buffer_planned; // Index of the optimally planned block
// Define planner variables // Define planner variables
typedef struct { typedef struct {
int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate
// from g-code position for movements requiring multiple line motions, // from g-code position for movements requiring multiple line motions,
// i.e. arcs, canned cycles, and backlash compensation. // i.e. arcs, canned cycles, and backlash compensation.
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
float previous_nominal_speed; // Nominal speed of previous path line segment float previous_nominal_speed; // Nominal speed of previous path line segment
} planner_t; } planner_t;
static planner_t pl; static planner_t pl;
// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer. // Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
uint8_t plan_next_block_index(uint8_t block_index) uint8_t plan_next_block_index(uint8_t block_index) {
{ block_index++;
block_index++; if (block_index == BLOCK_BUFFER_SIZE) {
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } block_index = 0;
return(block_index); }
return (block_index);
} }
// Returns the index of the previous block in the ring buffer // Returns the index of the previous block in the ring buffer
static uint8_t plan_prev_block_index(uint8_t block_index) static uint8_t plan_prev_block_index(uint8_t block_index) {
{ if (block_index == 0) {
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } block_index = BLOCK_BUFFER_SIZE;
block_index--; }
return(block_index); block_index--;
return (block_index);
} }
/* PLANNER SPEED DEFINITION /* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed +--------+ <- current->nominal_speed
/ \ / \
@ -123,181 +122,187 @@ static uint8_t plan_prev_block_index(uint8_t block_index)
ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more. ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
*/ */
static void planner_recalculate() static void planner_recalculate() {
{ // Initialize block index to the last block in the planner buffer.
// Initialize block index to the last block in the planner buffer. uint8_t block_index = plan_prev_block_index(block_buffer_head);
uint8_t block_index = plan_prev_block_index(block_buffer_head);
// Bail. Can't do anything with one only one plan-able block. // Bail. Can't do anything with one only one plan-able block.
if (block_index == block_buffer_planned) { return; } if (block_index == block_buffer_planned) {
return;
}
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached. // block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
// NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan. // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
float entry_speed_sqr; float entry_speed_sqr;
plan_block_t *next; plan_block_t *next;
plan_block_t *current = &block_buffer[block_index]; plan_block_t *current = &block_buffer[block_index];
// Calculate maximum entry speed for last block in buffer, where the exit speed is always zero. // Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters); current->entry_speed_sqr = min(current->max_entry_speed_sqr, 2 * current->acceleration * current->millimeters);
block_index = plan_prev_block_index(block_index); block_index = plan_prev_block_index(block_index);
if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete. if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
// Check if the first block is the tail. If so, notify stepper to update its current parameters. // Check if the first block is the tail. If so, notify stepper to update its current parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); } if (block_index == block_buffer_tail) {
} else { // Three or more plan-able blocks st_update_plan_block_parameters();
while (block_index != block_buffer_planned) {
next = current;
current = &block_buffer[block_index];
block_index = plan_prev_block_index(block_index);
// Check if next block is the tail block(=planned block). If so, update current stepper parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
if (entry_speed_sqr < current->max_entry_speed_sqr) {
current->entry_speed_sqr = entry_speed_sqr;
} else {
current->entry_speed_sqr = current->max_entry_speed_sqr;
} }
} } else { // Three or more plan-able blocks
} while (block_index != block_buffer_planned) {
} next = current;
current = &block_buffer[block_index];
block_index = plan_prev_block_index(block_index);
// Forward Pass: Forward plan the acceleration curve from the planned pointer onward. // Check if next block is the tail block(=planned block). If so, update current stepper parameters.
// Also scans for optimal plan breakpoints and appropriately updates the planned pointer. if (block_index == block_buffer_tail) {
next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer st_update_plan_block_parameters();
block_index = plan_next_block_index(block_buffer_planned); }
while (block_index != block_buffer_head) {
current = next;
next = &block_buffer[block_index];
// Any acceleration detected in the forward pass automatically moves the optimal planned // Compute maximum entry speed decelerating over the current block from its exit speed.
// pointer forward, since everything before this is all optimal. In other words, nothing if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
// can improve the plan from the buffer tail to the planned pointer by logic. entry_speed_sqr = next->entry_speed_sqr + 2 * current->acceleration * current->millimeters;
if (current->entry_speed_sqr < next->entry_speed_sqr) { if (entry_speed_sqr < current->max_entry_speed_sqr) {
entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters; current->entry_speed_sqr = entry_speed_sqr;
// If true, current block is full-acceleration and we can move the planned pointer forward. } else {
if (entry_speed_sqr < next->entry_speed_sqr) { current->entry_speed_sqr = current->max_entry_speed_sqr;
next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this. }
block_buffer_planned = block_index; // Set optimal plan pointer. }
} }
} }
// Any block set at its maximum entry speed also creates an optimal plan up to this // Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
// point in the buffer. When the plan is bracketed by either the beginning of the // Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
// buffer and a maximum entry speed or two maximum entry speeds, every block in between next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer
// cannot logically be further improved. Hence, we don't have to recompute them anymore. block_index = plan_next_block_index(block_buffer_planned);
if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; } while (block_index != block_buffer_head) {
block_index = plan_next_block_index( block_index ); current = next;
} next = &block_buffer[block_index];
// Any acceleration detected in the forward pass automatically moves the optimal planned
// pointer forward, since everything before this is all optimal. In other words, nothing
// can improve the plan from the buffer tail to the planned pointer by logic.
if (current->entry_speed_sqr < next->entry_speed_sqr) {
entry_speed_sqr = current->entry_speed_sqr + 2 * current->acceleration * current->millimeters;
// If true, current block is full-acceleration and we can move the planned pointer forward.
if (entry_speed_sqr < next->entry_speed_sqr) {
next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
block_buffer_planned = block_index; // Set optimal plan pointer.
}
}
// Any block set at its maximum entry speed also creates an optimal plan up to this
// point in the buffer. When the plan is bracketed by either the beginning of the
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
if (next->entry_speed_sqr == next->max_entry_speed_sqr) {
block_buffer_planned = block_index;
}
block_index = plan_next_block_index(block_index);
}
} }
void plan_reset() {
void plan_reset() memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
{ plan_reset_buffer();
memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
plan_reset_buffer();
} }
void plan_reset_buffer() {
void plan_reset_buffer() block_buffer_tail = 0;
{ block_buffer_head = 0; // Empty = tail
block_buffer_tail = 0; next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
block_buffer_head = 0; // Empty = tail block_buffer_planned = 0; // = block_buffer_tail;
next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
block_buffer_planned = 0; // = block_buffer_tail;
} }
void plan_discard_current_block() {
void plan_discard_current_block() if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
{ uint8_t block_index = plan_next_block_index(block_buffer_tail);
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer. // Push block_buffer_planned pointer, if encountered.
uint8_t block_index = plan_next_block_index( block_buffer_tail ); if (block_buffer_tail == block_buffer_planned) {
// Push block_buffer_planned pointer, if encountered. block_buffer_planned = block_index;
if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; } }
block_buffer_tail = block_index; block_buffer_tail = block_index;
} }
} }
// Returns address of planner buffer block used by system motions. Called by segment generator. // Returns address of planner buffer block used by system motions. Called by segment generator.
plan_block_t *plan_get_system_motion_block() plan_block_t *plan_get_system_motion_block() {
{ return (&block_buffer[block_buffer_head]);
return(&block_buffer[block_buffer_head]);
} }
// Returns address of first planner block, if available. Called by various main program functions. // Returns address of first planner block, if available. Called by various main program functions.
plan_block_t *plan_get_current_block() plan_block_t *plan_get_current_block() {
{ if (block_buffer_head == block_buffer_tail) {
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty return (NULL);
return(&block_buffer[block_buffer_tail]); } // Buffer empty
return (&block_buffer[block_buffer_tail]);
} }
float plan_get_exec_block_exit_speed_sqr() {
float plan_get_exec_block_exit_speed_sqr() uint8_t block_index = plan_next_block_index(block_buffer_tail);
{ if (block_index == block_buffer_head) {
uint8_t block_index = plan_next_block_index(block_buffer_tail); return (0.0);
if (block_index == block_buffer_head) { return( 0.0 ); } }
return( block_buffer[block_index].entry_speed_sqr ); return (block_buffer[block_index].entry_speed_sqr);
} }
// Returns the availability status of the block ring buffer. True, if full. // Returns the availability status of the block ring buffer. True, if full.
uint8_t plan_check_full_buffer() uint8_t plan_check_full_buffer() {
{ if (block_buffer_tail == next_buffer_head) {
if (block_buffer_tail == next_buffer_head) { return(true); } return (true);
return(false); }
return (false);
} }
// Computes and returns block nominal speed based on running condition and override values. // Computes and returns block nominal speed based on running condition and override values.
// NOTE: All system motion commands, such as homing/parking, are not subject to overrides. // NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
float plan_compute_profile_nominal_speed(plan_block_t *block) float plan_compute_profile_nominal_speed(plan_block_t *block) {
{ float nominal_speed = block->programmed_rate;
float nominal_speed = block->programmed_rate; if (block->condition & PL_COND_FLAG_RAPID_MOTION) {
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); } nominal_speed *= (0.01 * sys.r_override);
else { } else {
if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); } if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) {
if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; } nominal_speed *= (0.01 * sys.f_override);
} }
if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); } if (nominal_speed > block->rapid_rate) {
return(MINIMUM_FEED_RATE); nominal_speed = block->rapid_rate;
}
}
if (nominal_speed > MINIMUM_FEED_RATE) {
return (nominal_speed);
}
return (MINIMUM_FEED_RATE);
} }
// Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's // Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's
// previous and current nominal speeds and max junction speed. // previous and current nominal speeds and max junction speed.
static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed) static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed) {
{ // Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds. if (nominal_speed > prev_nominal_speed) {
if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; } block->max_entry_speed_sqr = prev_nominal_speed * prev_nominal_speed;
else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; } } else {
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) { block->max_entry_speed_sqr = block->max_junction_speed_sqr; } block->max_entry_speed_sqr = nominal_speed * nominal_speed;
}
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) {
block->max_entry_speed_sqr = block->max_junction_speed_sqr;
}
} }
// Re-calculates buffered motions profile parameters upon a motion-based override change. // Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters() void plan_update_velocity_profile_parameters() {
{ uint8_t block_index = block_buffer_tail;
uint8_t block_index = block_buffer_tail; plan_block_t *block;
plan_block_t *block; float nominal_speed;
float nominal_speed; float prev_nominal_speed = SOME_LARGE_VALUE; // Set high for first block nominal speed calculation.
float prev_nominal_speed = SOME_LARGE_VALUE; // Set high for first block nominal speed calculation. while (block_index != block_buffer_head) {
while (block_index != block_buffer_head) { block = &block_buffer[block_index];
block = &block_buffer[block_index]; nominal_speed = plan_compute_profile_nominal_speed(block);
nominal_speed = plan_compute_profile_nominal_speed(block); plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed);
plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed); prev_nominal_speed = nominal_speed;
prev_nominal_speed = nominal_speed; block_index = plan_next_block_index(block_index);
block_index = plan_next_block_index(block_index); }
} pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
} }
/* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position /* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed 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.
@ -312,211 +317,221 @@ void plan_update_velocity_profile_parameters()
head. It avoids changing the planner state and preserves the buffer to ensure subsequent gcode 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 motions are still planned correctly, while the stepper module only points to the block buffer head
to execute the special system motion. */ to execute the special system motion. */
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) {
{ // Prepare and initialize new block. Copy relevant pl_data for block execution.
// Prepare and initialize new block. Copy relevant pl_data for block execution. plan_block_t *block = &block_buffer[block_buffer_head];
plan_block_t *block = &block_buffer[block_buffer_head]; memset(block, 0, sizeof(plan_block_t)); // Zero all block values.
memset(block,0,sizeof(plan_block_t)); // Zero all block values. block->condition = pl_data->condition;
block->condition = pl_data->condition; #ifdef VARIABLE_SPINDLE
#ifdef VARIABLE_SPINDLE
block->spindle_speed = pl_data->spindle_speed; block->spindle_speed = pl_data->spindle_speed;
#endif #endif
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
block->line_number = pl_data->line_number; block->line_number = pl_data->line_number;
#endif #endif
// Compute and store initial move distance data. // Compute and store initial move distance data.
int32_t target_steps[N_AXIS], position_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. // Copy position data based on type of motion being planned.
if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) { if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) {
#ifdef COREXY #ifdef COREXY
position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position); position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position); position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
position_steps[Z_AXIS] = sys_position[Z_AXIS]; position_steps[Z_AXIS] = sys_position[Z_AXIS];
#else #else
memcpy(position_steps, sys_position, sizeof(sys_position)); memcpy(position_steps, sys_position, sizeof(sys_position));
#endif #endif
} else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
#ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
#endif
for (idx=0; idx<N_AXIS; idx++) {
// Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
// Also, compute individual axes distance for move and prep unit vector calculations.
// NOTE: Computes true distance from converted step values.
#ifdef COREXY
if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
}
block->step_event_count = max(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) {
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx];
} else if (idx == B_MOTOR) {
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx];
} else {
delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
}
#else
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
block->step_event_count = max(block->step_event_count, block->steps[idx]);
delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
#endif
unit_vec[idx] = delta_mm; // Store unit vector numerator
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
}
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
// NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec);
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
// Store programmed rate.
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
else {
block->programmed_rate = pl_data->feed_rate;
if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
}
// TODO: Need to check this method handling zero junction speeds when starting from rest.
if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
// If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
} else {
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
// deviation is defined as the distance from the junction to the closest edge of the circle,
// colinear with the circle center. The circular segment joining the two paths represents the
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
// path width or max_jerk in the previous Grbl version. This approach does not actually deviate
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
// nonlinearities of both the junction angle and junction velocity.
//
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
// is exactly the same. Instead of motioning all the way to junction point, the machine will
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
// a continuous mode path, but ARM-based microcontrollers most certainly do.
//
// NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
// changed dynamically during operation nor can the line move geometry. This must be kept in
// memory in the event of a feedrate override changing the nominal speeds of blocks, which can
// change the overall maximum entry speed conditions of all blocks.
float junction_unit_vec[N_AXIS];
float junction_cos_theta = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
junction_cos_theta -= pl.previous_unit_vec[idx]*unit_vec[idx];
junction_unit_vec[idx] = unit_vec[idx]-pl.previous_unit_vec[idx];
}
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999) {
// For a 0 degree acute junction, just set minimum junction speed.
block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
} else { } else {
if (junction_cos_theta < -0.999999) { memcpy(position_steps, pl.position, sizeof(pl.position));
// Junction is a straight line or 180 degrees. Junction speed is infinite.
block->max_junction_speed_sqr = SOME_LARGE_VALUE;
} else {
convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
(junction_acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
}
} }
}
// Block system motion from updating this data to ensure next g-code motion is computed correctly. #ifdef COREXY
if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) { target_steps[A_MOTOR] = lround(target[A_MOTOR] * settings.steps_per_mm[A_MOTOR]);
float nominal_speed = plan_compute_profile_nominal_speed(block); target_steps[B_MOTOR] = lround(target[B_MOTOR] * settings.steps_per_mm[B_MOTOR]);
plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed); block->steps[A_MOTOR] =
pl.previous_nominal_speed = nominal_speed; labs((target_steps[X_AXIS] - position_steps[X_AXIS]) + (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
block->steps[B_MOTOR] =
// Update previous path unit_vector and planner position. labs((target_steps[X_AXIS] - position_steps[X_AXIS]) - (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[] #endif
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. for (idx = 0; idx < N_AXIS; idx++) {
block_buffer_head = next_buffer_head; // Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
next_buffer_head = plan_next_block_index(block_buffer_head); // Also, compute individual axes distance for move and prep unit vector calculations.
// NOTE: Computes true distance from converted step values.
#ifdef COREXY
if (!(idx == A_MOTOR) && !(idx == B_MOTOR)) {
target_steps[idx] = lround(target[idx] * settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx] - position_steps[idx]);
}
block->step_event_count = max(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) {
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] + target_steps[Y_AXIS] - position_steps[Y_AXIS]) /
settings.steps_per_mm[idx];
} else if (idx == B_MOTOR) {
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] - target_steps[Y_AXIS] + position_steps[Y_AXIS]) /
settings.steps_per_mm[idx];
} else {
delta_mm = (target_steps[idx] - position_steps[idx]) / settings.steps_per_mm[idx];
}
#else
target_steps[idx] = lround(target[idx] * settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx] - position_steps[idx]);
block->step_event_count = max(block->step_event_count, block->steps[idx]);
delta_mm = (target_steps[idx] - position_steps[idx]) / settings.steps_per_mm[idx];
#endif
unit_vec[idx] = delta_mm; // Store unit vector numerator
// Finish up by recalculating the plan with the new block. // Set direction bits. Bit enabled always means direction is negative.
planner_recalculate(); if (delta_mm < 0.0) {
} block->direction_bits |= get_direction_pin_mask(idx);
return(PLAN_OK); }
}
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) {
return (PLAN_EMPTY_BLOCK);
}
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
// NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec);
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
// Store programmed rate.
if (block->condition & PL_COND_FLAG_RAPID_MOTION) {
block->programmed_rate = block->rapid_rate;
} else {
block->programmed_rate = pl_data->feed_rate;
if (block->condition & PL_COND_FLAG_INVERSE_TIME) {
block->programmed_rate *= block->millimeters;
}
}
// TODO: Need to check this method handling zero junction speeds when starting from rest.
if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
// If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
} else {
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
// deviation is defined as the distance from the junction to the closest edge of the circle,
// colinear with the circle center. The circular segment joining the two paths represents the
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
// path width or max_jerk in the previous Grbl version. This approach does not actually deviate
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
// nonlinearities of both the junction angle and junction velocity.
//
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
// is exactly the same. Instead of motioning all the way to junction point, the machine will
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
// a continuous mode path, but ARM-based microcontrollers most certainly do.
//
// NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
// changed dynamically during operation nor can the line move geometry. This must be kept in
// memory in the event of a feedrate override changing the nominal speeds of blocks, which can
// change the overall maximum entry speed conditions of all blocks.
float junction_unit_vec[N_AXIS];
float junction_cos_theta = 0.0;
for (idx = 0; idx < N_AXIS; idx++) {
junction_cos_theta -= pl.previous_unit_vec[idx] * unit_vec[idx];
junction_unit_vec[idx] = unit_vec[idx] - pl.previous_unit_vec[idx];
}
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999) {
// For a 0 degree acute junction, just set minimum junction speed.
block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED;
} else {
if (junction_cos_theta < -0.999999) {
// Junction is a straight line or 180 degrees. Junction speed is infinite.
block->max_junction_speed_sqr = SOME_LARGE_VALUE;
} else {
convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
float sin_theta_d2 =
sqrt(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr =
max(MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED,
(junction_acceleration * settings.junction_deviation * sin_theta_d2) / (1.0 - sin_theta_d2));
}
}
}
// Block system motion from updating this data to ensure next g-code motion is computed correctly.
if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
float nominal_speed = plan_compute_profile_nominal_speed(block);
plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed);
pl.previous_nominal_speed = nominal_speed;
// Update previous path unit_vector and planner position.
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
// New block is all set. Update buffer head and next buffer head indices.
block_buffer_head = next_buffer_head;
next_buffer_head = plan_next_block_index(block_buffer_head);
// Finish up by recalculating the plan with the new block.
planner_recalculate();
}
return (PLAN_OK);
} }
// Reset the planner position vectors. Called by the system abort/initialization routine. // Reset the planner position vectors. Called by the system abort/initialization routine.
void plan_sync_position() void plan_sync_position() {
{ // TODO: For motor configurations not in the same coordinate frame as the machine position,
// TODO: For motor configurations not in the same coordinate frame as the machine position, // this function needs to be updated to accomodate the difference.
// this function needs to be updated to accomodate the difference. uint8_t idx;
uint8_t idx; for (idx = 0; idx < N_AXIS; idx++) {
for (idx=0; idx<N_AXIS; idx++) { #ifdef COREXY
#ifdef COREXY if (idx == X_AXIS) {
if (idx==X_AXIS) { pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position); } else if (idx == Y_AXIS) {
} else if (idx==Y_AXIS) { pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position); } else {
} else { pl.position[idx] = sys_position[idx];
}
#else
pl.position[idx] = sys_position[idx]; pl.position[idx] = sys_position[idx];
} #endif
#else }
pl.position[idx] = sys_position[idx];
#endif
}
} }
// Returns the number of available blocks are in the planner buffer. // Returns the number of available blocks are in the planner buffer.
uint8_t plan_get_block_buffer_available() uint8_t plan_get_block_buffer_available() {
{ if (block_buffer_head >= block_buffer_tail) {
if (block_buffer_head >= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); } return ((BLOCK_BUFFER_SIZE - 1) - (block_buffer_head - block_buffer_tail));
return((block_buffer_tail-block_buffer_head-1)); }
return ((block_buffer_tail - block_buffer_head - 1));
} }
// Returns the number of active blocks are in the planner buffer. // Returns the number of active blocks are in the planner buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h // NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
uint8_t plan_get_block_buffer_count() uint8_t plan_get_block_buffer_count() {
{ if (block_buffer_head >= block_buffer_tail) {
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); } return (block_buffer_head - block_buffer_tail);
return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head)); }
return (BLOCK_BUFFER_SIZE - (block_buffer_tail - block_buffer_head));
} }
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
void plan_cycle_reinitialize() void plan_cycle_reinitialize() {
{ // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
// Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer. st_update_plan_block_parameters();
st_update_plan_block_parameters(); block_buffer_planned = block_buffer_tail;
block_buffer_planned = block_buffer_tail; planner_recalculate();
planner_recalculate();
} }

View file

@ -22,83 +22,80 @@
#ifndef planner_h #ifndef planner_h
#define planner_h #define planner_h
// The number of linear motions that can be in the plan at any give time // The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE #ifndef BLOCK_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
#define BLOCK_BUFFER_SIZE 15 #define BLOCK_BUFFER_SIZE 15
#else #else
#define BLOCK_BUFFER_SIZE 16 #define BLOCK_BUFFER_SIZE 16
#endif #endif
#endif #endif
// Returned status message from planner. // Returned status message from planner.
#define PLAN_OK true #define PLAN_OK true
#define PLAN_EMPTY_BLOCK false #define PLAN_EMPTY_BLOCK false
// Define planner data condition flags. Used to denote running conditions of a block. // Define planner data condition flags. Used to denote running conditions of a block.
#define PL_COND_FLAG_RAPID_MOTION bit(0) #define PL_COND_FLAG_RAPID_MOTION bit(0)
#define PL_COND_FLAG_SYSTEM_MOTION bit(1) // Single motion. Circumvents planner state. Used by home/park. #define PL_COND_FLAG_SYSTEM_MOTION bit(1) // Single motion. Circumvents planner state. Used by home/park.
#define PL_COND_FLAG_NO_FEED_OVERRIDE bit(2) // Motion does not honor feed override. #define PL_COND_FLAG_NO_FEED_OVERRIDE bit(2) // Motion does not honor feed override.
#define PL_COND_FLAG_INVERSE_TIME bit(3) // Interprets feed rate value as inverse time when set. #define PL_COND_FLAG_INVERSE_TIME bit(3) // Interprets feed rate value as inverse time when set.
#define PL_COND_FLAG_SPINDLE_CW bit(4) #define PL_COND_FLAG_SPINDLE_CW bit(4)
#define PL_COND_FLAG_SPINDLE_CCW bit(5) #define PL_COND_FLAG_SPINDLE_CCW bit(5)
#define PL_COND_FLAG_COOLANT_FLOOD bit(6) #define PL_COND_FLAG_COOLANT_FLOOD bit(6)
#define PL_COND_FLAG_COOLANT_MIST bit(7) #define PL_COND_FLAG_COOLANT_MIST bit(7)
#define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION|PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE) #define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION | PL_COND_FLAG_SYSTEM_MOTION | PL_COND_FLAG_NO_FEED_OVERRIDE)
#define PL_COND_SPINDLE_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW) #define PL_COND_SPINDLE_MASK (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)
#define PL_COND_ACCESSORY_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW|PL_COND_FLAG_COOLANT_FLOOD|PL_COND_FLAG_COOLANT_MIST) #define PL_COND_ACCESSORY_MASK \
(PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW | PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST)
// This struct stores a linear movement of a g-code block motion with its critical "nominal" values // 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 {
// Fields used by the bresenham algorithm for tracing the line // Fields used by the bresenham algorithm for tracing the line
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
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.
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
// Block condition data to ensure correct execution depending on states and overrides. // Block condition data to ensure correct execution depending on states and overrides.
uint8_t condition; // Block bitflag variable defining block run conditions. Copied from pl_line_data. uint8_t condition; // Block bitflag variable defining block run conditions. Copied from pl_line_data.
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
int32_t line_number; // Block line number for real-time reporting. Copied from pl_line_data. int32_t line_number; // Block line number for real-time reporting. Copied from pl_line_data.
#endif #endif
// Fields used by the motion planner to manage acceleration. Some of these values may be updated // 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. // 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
float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change. float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
float millimeters; // The remaining distance for this block to be executed in (mm). float millimeters; // The remaining distance for this block to be executed in (mm).
// NOTE: This value may be altered by stepper algorithm during execution. // NOTE: This value may be altered by stepper algorithm during execution.
// Stored rate limiting data used by planner when changes occur. // Stored rate limiting data used by planner when changes occur.
float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2 float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min) float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min)
float programmed_rate; // Programmed rate of this block (mm/min). float programmed_rate; // Programmed rate of this block (mm/min).
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Stored spindle speed data used by spindle overrides and resuming methods. // Stored spindle speed data used by spindle overrides and resuming methods.
float spindle_speed; // Block spindle speed. Copied from pl_line_data. float spindle_speed; // Block spindle speed. Copied from pl_line_data.
#endif #endif
} plan_block_t; } plan_block_t;
// Planner data prototype. Must be used when passing new motions to the planner. // Planner data prototype. Must be used when passing new motions to the planner.
typedef struct { typedef struct {
float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion. float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion.
float spindle_speed; // Desired spindle speed through line motion. float spindle_speed; // Desired spindle speed through line motion.
uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above. uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above.
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
int32_t line_number; // Desired line number to report when executing. int32_t line_number; // Desired line number to report when executing.
#endif #endif
} plan_line_data_t; } plan_line_data_t;
// Initialize and reset the motion plan subsystem // Initialize and reset the motion plan subsystem
void plan_reset(); // Reset all void plan_reset(); // Reset all
void plan_reset_buffer(); // Reset buffer only. void plan_reset_buffer(); // Reset buffer only.
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position // Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
@ -146,5 +143,4 @@ uint8_t plan_check_full_buffer();
void plan_get_planner_mpos(float *target); void plan_get_planner_mpos(float *target);
#endif #endif

View file

@ -21,23 +21,16 @@
#include "grbl.h" #include "grbl.h"
void printString(const char *s) {
void printString(const char *s) while (*s) serial_write(*s++);
{
while (*s)
serial_write(*s++);
} }
// Print a string stored in PGM-memory // Print a string stored in PGM-memory
void printPgmString(const char *s) void printPgmString(const char *s) {
{ char c;
char c; while ((c = pgm_read_byte_near(s++))) serial_write(c);
while ((c = pgm_read_byte_near(s++)))
serial_write(c);
} }
// void printIntegerInBase(unsigned long n, unsigned long base) // void printIntegerInBase(unsigned long n, unsigned long base)
// { // {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. // unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
@ -59,133 +52,129 @@ void printPgmString(const char *s)
// 'A' + buf[i - 1] - 10); // 'A' + buf[i - 1] - 10);
// } // }
// Prints an uint8 variable in base 10. // Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n) void print_uint8_base10(uint8_t n) {
{ uint8_t digit_a = 0;
uint8_t digit_a = 0; uint8_t digit_b = 0;
uint8_t digit_b = 0; if (n >= 100) { // 100-255
if (n >= 100) { // 100-255 digit_a = '0' + n % 10;
digit_a = '0' + n % 10; n /= 10;
n /= 10; }
} if (n >= 10) { // 10-99
if (n >= 10) { // 10-99 digit_b = '0' + n % 10;
digit_b = '0' + n % 10; n /= 10;
n /= 10; }
} serial_write('0' + n);
serial_write('0' + n); if (digit_b) {
if (digit_b) { serial_write(digit_b); } serial_write(digit_b);
if (digit_a) { serial_write(digit_a); } }
if (digit_a) {
serial_write(digit_a);
}
} }
// Prints an uint8 variable in base 2 with desired number of desired digits. // Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) { void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) {
unsigned char buf[digits]; unsigned char buf[digits];
uint8_t i = 0; uint8_t i = 0;
for (; i < digits; i++) { for (; i < digits; i++) {
buf[i] = n % 2 ; buf[i] = n % 2;
n /= 2; n /= 2;
} }
for (; i > 0; i--) for (; i > 0; i--) serial_write('0' + buf[i - 1]);
serial_write('0' + buf[i - 1]);
} }
void print_uint32_base10(uint32_t n) {
if (n == 0) {
serial_write('0');
return;
}
void print_uint32_base10(uint32_t n) unsigned char buf[10];
{ uint8_t i = 0;
if (n == 0) {
serial_write('0');
return;
}
unsigned char buf[10]; while (n > 0) {
uint8_t i = 0; buf[i++] = n % 10;
n /= 10;
}
while (n > 0) { for (; i > 0; i--) serial_write('0' + buf[i - 1]);
buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
serial_write('0' + buf[i-1]);
} }
void printInteger(long n) {
void printInteger(long n) if (n < 0) {
{ serial_write('-');
if (n < 0) { print_uint32_base10(-n);
serial_write('-'); } else {
print_uint32_base10(-n); print_uint32_base10(n);
} else { }
print_uint32_base10(n);
}
} }
// Convert float to string by immediately converting to a long integer, which contains // Convert float to string by immediately converting to a long integer, which contains
// more digits than a float. Number of decimal places, which are tracked by a counter, // more digits than a float. Number of decimal places, which are tracked by a counter,
// may be set by the user. The integer is then efficiently converted to a string. // may be set by the user. The integer is then efficiently converted to a string.
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up // NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// techniques are actually just slightly slower. Found this out the hard way. // techniques are actually just slightly slower. Found this out the hard way.
void printFloat(float n, uint8_t decimal_places) void printFloat(float n, uint8_t decimal_places) {
{ if (n < 0) {
if (n < 0) { serial_write('-');
serial_write('-'); n = -n;
n = -n; }
}
uint8_t decimals = decimal_places; uint8_t decimals = decimal_places;
while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4. while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4.
n *= 100; n *= 100;
decimals -= 2; decimals -= 2;
} }
if (decimals) { n *= 10; } if (decimals) {
n += 0.5; // Add rounding factor. Ensures carryover through entire value. n *= 10;
}
n += 0.5; // Add rounding factor. Ensures carryover through entire value.
// Generate digits backwards and store in string. // Generate digits backwards and store in string.
unsigned char buf[13]; unsigned char buf[13];
uint8_t i = 0; uint8_t i = 0;
uint32_t a = (long)n; uint32_t a = (long)n;
while(a > 0) { while (a > 0) {
buf[i++] = (a % 10) + '0'; // Get digit buf[i++] = (a % 10) + '0'; // Get digit
a /= 10; a /= 10;
} }
while (i < decimal_places) { while (i < decimal_places) {
buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1) buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1)
} }
if (i == decimal_places) { // Fill in leading zero, if needed. if (i == decimal_places) { // Fill in leading zero, if needed.
buf[i++] = '0'; buf[i++] = '0';
} }
// Print the generated string. // Print the generated string.
for (; i > 0; i--) { for (; i > 0; i--) {
if (i == decimal_places) { serial_write('.'); } // Insert decimal point in right place. if (i == decimal_places) {
serial_write(buf[i-1]); serial_write('.');
} } // Insert decimal point in right place.
serial_write(buf[i - 1]);
}
} }
// Floating value printing handlers for special variables types used in Grbl and are defined // Floating value printing handlers for special variables types used in Grbl and are defined
// in the config.h. // in the config.h.
// - 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.
void printFloat_CoordValue(float n) { void printFloat_CoordValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH); printFloat(n * INCH_PER_MM, N_DECIMAL_COORDVALUE_INCH);
} else { } else {
printFloat(n,N_DECIMAL_COORDVALUE_MM); printFloat(n, N_DECIMAL_COORDVALUE_MM);
} }
} }
void printFloat_RateValue(float n) { void printFloat_RateValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH); printFloat(n * INCH_PER_MM, N_DECIMAL_RATEVALUE_INCH);
} else { } else {
printFloat(n,N_DECIMAL_RATEVALUE_MM); printFloat(n, N_DECIMAL_RATEVALUE_MM);
} }
} }
// Debug tool to print free memory in bytes at the called point. // Debug tool to print free memory in bytes at the called point.

View file

@ -22,7 +22,6 @@
#ifndef print_h #ifndef print_h
#define print_h #define print_h
void printString(const char *s); void printString(const char *s);
void printPgmString(const char *s); void printPgmString(const char *s);

View file

@ -20,47 +20,45 @@
#include "grbl.h" #include "grbl.h"
// Inverts the probe pin state depending on user settings and probing cycle mode. // Inverts the probe pin state depending on user settings and probing cycle mode.
uint8_t probe_invert_mask; uint8_t probe_invert_mask;
// Probe pin initialization routine. // Probe pin initialization routine.
void probe_init() void probe_init() {
{ PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins #ifdef DISABLE_PROBE_PIN_PULL_UP
#ifdef DISABLE_PROBE_PIN_PULL_UP
PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down. PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down.
#else #else
PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation. PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation.
#endif #endif
probe_configure_invert_mask(false); // Initialize invert mask. probe_configure_invert_mask(false); // Initialize invert mask.
} }
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to // Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation // appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece. // and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away) void probe_configure_invert_mask(uint8_t is_probe_away) {
{ probe_invert_mask = 0; // Initialize as zero.
probe_invert_mask = 0; // Initialize as zero. if (bit_isfalse(settings.flags, BITFLAG_INVERT_PROBE_PIN)) {
if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; } probe_invert_mask ^= PROBE_MASK;
if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; } }
if (is_probe_away) {
probe_invert_mask ^= PROBE_MASK;
}
} }
// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor. // Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask); } uint8_t probe_get_state() {
return ((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask);
}
// Monitors probe pin state and records the system position when detected. Called by the // Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick. // stepper ISR per ISR tick.
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR. // NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
void probe_state_monitor() void probe_state_monitor() {
{ if (probe_get_state()) {
if (probe_get_state()) { sys_probe_state = PROBE_OFF;
sys_probe_state = PROBE_OFF; memcpy(sys_probe_position, sys_position, sizeof(sys_position));
memcpy(sys_probe_position, sys_position, sizeof(sys_position)); bit_true(sys_rt_exec_state, EXEC_MOTION_CANCEL);
bit_true(sys_rt_exec_state, EXEC_MOTION_CANCEL); }
}
} }

View file

@ -22,8 +22,8 @@
#define probe_h #define probe_h
// Values that define the probing state machine. // Values that define the probing state machine.
#define PROBE_OFF 0 // Probing disabled or not in use. (Must be zero.) #define PROBE_OFF 0 // Probing disabled or not in use. (Must be zero.)
#define PROBE_ACTIVE 1 // Actively watching the input pin. #define PROBE_ACTIVE 1 // Actively watching the input pin.
// Probe pin initialization routine. // Probe pin initialization routine.
void probe_init(); void probe_init();

File diff suppressed because it is too large Load diff

View file

@ -29,7 +29,7 @@
// memory space we can invest into here or we re-write the g-code parser not to have this // memory space we can invest into here or we re-write the g-code parser not to have this
// buffer. // buffer.
#ifndef LINE_BUFFER_SIZE #ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE 80 #define LINE_BUFFER_SIZE 80
#endif #endif
// Starts Grbl main loop. It handles all incoming characters from the serial port and executes // Starts Grbl main loop. It handles all incoming characters from the serial port and executes

File diff suppressed because it is too large Load diff

View file

@ -21,68 +21,68 @@
#define report_h #define report_h
// Define Grbl status codes. Valid values (0-255) // Define Grbl status codes. Valid values (0-255)
#define STATUS_OK 0 #define STATUS_OK 0
#define STATUS_EXPECTED_COMMAND_LETTER 1 #define STATUS_EXPECTED_COMMAND_LETTER 1
#define STATUS_BAD_NUMBER_FORMAT 2 #define STATUS_BAD_NUMBER_FORMAT 2
#define STATUS_INVALID_STATEMENT 3 #define STATUS_INVALID_STATEMENT 3
#define STATUS_NEGATIVE_VALUE 4 #define STATUS_NEGATIVE_VALUE 4
#define STATUS_SETTING_DISABLED 5 #define STATUS_SETTING_DISABLED 5
#define STATUS_SETTING_STEP_PULSE_MIN 6 #define STATUS_SETTING_STEP_PULSE_MIN 6
#define STATUS_SETTING_READ_FAIL 7 #define STATUS_SETTING_READ_FAIL 7
#define STATUS_IDLE_ERROR 8 #define STATUS_IDLE_ERROR 8
#define STATUS_SYSTEM_GC_LOCK 9 #define STATUS_SYSTEM_GC_LOCK 9
#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_CHECK_DOOR 13
#define STATUS_LINE_LENGTH_EXCEEDED 14 #define STATUS_LINE_LENGTH_EXCEEDED 14
#define STATUS_TRAVEL_EXCEEDED 15 #define STATUS_TRAVEL_EXCEEDED 15
#define STATUS_INVALID_JOG_COMMAND 16 #define STATUS_INVALID_JOG_COMMAND 16
#define STATUS_SETTING_DISABLED_LASER 17 #define STATUS_SETTING_DISABLED_LASER 17
#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
#define STATUS_GCODE_UNDEFINED_FEED_RATE 22 #define STATUS_GCODE_UNDEFINED_FEED_RATE 22
#define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23 #define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23
#define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24 #define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24
#define STATUS_GCODE_WORD_REPEATED 25 #define STATUS_GCODE_WORD_REPEATED 25
#define STATUS_GCODE_NO_AXIS_WORDS 26 #define STATUS_GCODE_NO_AXIS_WORDS 26
#define STATUS_GCODE_INVALID_LINE_NUMBER 27 #define STATUS_GCODE_INVALID_LINE_NUMBER 27
#define STATUS_GCODE_VALUE_WORD_MISSING 28 #define STATUS_GCODE_VALUE_WORD_MISSING 28
#define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29 #define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29
#define STATUS_GCODE_G53_INVALID_MOTION_MODE 30 #define STATUS_GCODE_G53_INVALID_MOTION_MODE 30
#define STATUS_GCODE_AXIS_WORDS_EXIST 31 #define STATUS_GCODE_AXIS_WORDS_EXIST 31
#define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32 #define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32
#define STATUS_GCODE_INVALID_TARGET 33 #define STATUS_GCODE_INVALID_TARGET 33
#define STATUS_GCODE_ARC_RADIUS_ERROR 34 #define STATUS_GCODE_ARC_RADIUS_ERROR 34
#define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35 #define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35
#define STATUS_GCODE_UNUSED_WORDS 36 #define STATUS_GCODE_UNUSED_WORDS 36
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37 #define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37
#define STATUS_GCODE_MAX_VALUE_EXCEEDED 38 #define STATUS_GCODE_MAX_VALUE_EXCEEDED 38
// Define Grbl alarm codes. Valid values (1-255). 0 is reserved. // Define Grbl alarm codes. Valid values (1-255). 0 is reserved.
#define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT #define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT
#define ALARM_SOFT_LIMIT_ERROR EXEC_ALARM_SOFT_LIMIT #define ALARM_SOFT_LIMIT_ERROR EXEC_ALARM_SOFT_LIMIT
#define ALARM_ABORT_CYCLE EXEC_ALARM_ABORT_CYCLE #define ALARM_ABORT_CYCLE EXEC_ALARM_ABORT_CYCLE
#define ALARM_PROBE_FAIL_INITIAL EXEC_ALARM_PROBE_FAIL_INITIAL #define ALARM_PROBE_FAIL_INITIAL EXEC_ALARM_PROBE_FAIL_INITIAL
#define ALARM_PROBE_FAIL_CONTACT EXEC_ALARM_PROBE_FAIL_CONTACT #define ALARM_PROBE_FAIL_CONTACT EXEC_ALARM_PROBE_FAIL_CONTACT
#define ALARM_HOMING_FAIL_RESET EXEC_ALARM_HOMING_FAIL_RESET #define ALARM_HOMING_FAIL_RESET EXEC_ALARM_HOMING_FAIL_RESET
#define ALARM_HOMING_FAIL_DOOR EXEC_ALARM_HOMING_FAIL_DOOR #define ALARM_HOMING_FAIL_DOOR EXEC_ALARM_HOMING_FAIL_DOOR
#define ALARM_HOMING_FAIL_PULLOFF EXEC_ALARM_HOMING_FAIL_PULLOFF #define ALARM_HOMING_FAIL_PULLOFF EXEC_ALARM_HOMING_FAIL_PULLOFF
#define ALARM_HOMING_FAIL_APPROACH EXEC_ALARM_HOMING_FAIL_APPROACH #define ALARM_HOMING_FAIL_APPROACH EXEC_ALARM_HOMING_FAIL_APPROACH
// Define Grbl feedback message codes. Valid values (0-255). // Define Grbl feedback message codes. Valid values (0-255).
#define MESSAGE_CRITICAL_EVENT 1 #define MESSAGE_CRITICAL_EVENT 1
#define MESSAGE_ALARM_LOCK 2 #define MESSAGE_ALARM_LOCK 2
#define MESSAGE_ALARM_UNLOCK 3 #define MESSAGE_ALARM_UNLOCK 3
#define MESSAGE_ENABLED 4 #define MESSAGE_ENABLED 4
#define MESSAGE_DISABLED 5 #define MESSAGE_DISABLED 5
#define MESSAGE_SAFETY_DOOR_AJAR 6 #define MESSAGE_SAFETY_DOOR_AJAR 6
#define MESSAGE_CHECK_LIMITS 7 #define MESSAGE_CHECK_LIMITS 7
#define MESSAGE_PROGRAM_END 8 #define MESSAGE_PROGRAM_END 8
#define MESSAGE_RESTORE_DEFAULTS 9 #define MESSAGE_RESTORE_DEFAULTS 9
#define MESSAGE_SPINDLE_RESTORE 10 #define MESSAGE_SPINDLE_RESTORE 10
#define MESSAGE_SLEEP_MODE 11 #define MESSAGE_SLEEP_MODE 11
// Prints system status messages. // Prints system status messages.
void report_status_message(uint8_t status_code); void report_status_message(uint8_t status_code);
@ -125,7 +125,7 @@ void report_execute_startup_message(char *line, uint8_t status_code);
void report_build_info(char *line); void report_build_info(char *line);
#ifdef DEBUG #ifdef DEBUG
void report_realtime_debug(); void report_realtime_debug();
#endif #endif
#endif #endif

View file

@ -21,184 +21,200 @@
#include "grbl.h" #include "grbl.h"
#define RX_RING_BUFFER (RX_BUFFER_SIZE+1) #define RX_RING_BUFFER (RX_BUFFER_SIZE + 1)
#define TX_RING_BUFFER (TX_BUFFER_SIZE+1) #define TX_RING_BUFFER (TX_BUFFER_SIZE + 1)
uint8_t serial_rx_buffer[RX_RING_BUFFER]; uint8_t serial_rx_buffer[RX_RING_BUFFER];
uint8_t serial_rx_buffer_head = 0; uint8_t serial_rx_buffer_head = 0;
volatile uint8_t serial_rx_buffer_tail = 0; volatile uint8_t serial_rx_buffer_tail = 0;
uint8_t serial_tx_buffer[TX_RING_BUFFER]; uint8_t serial_tx_buffer[TX_RING_BUFFER];
uint8_t serial_tx_buffer_head = 0; uint8_t serial_tx_buffer_head = 0;
volatile uint8_t serial_tx_buffer_tail = 0; volatile uint8_t serial_tx_buffer_tail = 0;
// Returns the number of bytes available in the RX serial buffer. // Returns the number of bytes available in the RX serial buffer.
uint8_t serial_get_rx_buffer_available() uint8_t serial_get_rx_buffer_available() {
{ uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile if (serial_rx_buffer_head >= rtail) {
if (serial_rx_buffer_head >= rtail) { return(RX_BUFFER_SIZE - (serial_rx_buffer_head-rtail)); } return (RX_BUFFER_SIZE - (serial_rx_buffer_head - rtail));
return((rtail-serial_rx_buffer_head-1)); }
return ((rtail - serial_rx_buffer_head - 1));
} }
// Returns the number of bytes used in the RX serial buffer. // Returns the number of bytes used in the RX serial buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h. // NOTE: Deprecated. Not used unless classic status reports are enabled in config.h.
uint8_t serial_get_rx_buffer_count() uint8_t serial_get_rx_buffer_count() {
{ uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile if (serial_rx_buffer_head >= rtail) {
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); } return (serial_rx_buffer_head - rtail);
return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head)); }
return (RX_BUFFER_SIZE - (rtail - serial_rx_buffer_head));
} }
// Returns the number of bytes used in the TX serial buffer. // Returns the number of bytes used in the TX serial buffer.
// NOTE: Not used except for debugging and ensuring no TX bottlenecks. // NOTE: Not used except for debugging and ensuring no TX bottlenecks.
uint8_t serial_get_tx_buffer_count() uint8_t serial_get_tx_buffer_count() {
{ uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile if (serial_tx_buffer_head >= ttail) {
if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); } return (serial_tx_buffer_head - ttail);
return (TX_RING_BUFFER - (ttail-serial_tx_buffer_head)); }
return (TX_RING_BUFFER - (ttail - serial_tx_buffer_head));
} }
void serial_init() {
void serial_init() // Set baud rate
{ #if BAUD_RATE < 57600
// Set baud rate uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1) / 2;
#if BAUD_RATE < 57600
uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ;
UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX
#else #else
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2; uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1) / 2;
UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200 UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200
#endif #endif
UBRR0H = UBRR0_value >> 8; UBRR0H = UBRR0_value >> 8;
UBRR0L = UBRR0_value; UBRR0L = UBRR0_value;
// enable rx, tx, and interrupt on complete reception of a byte // enable rx, tx, and interrupt on complete reception of a byte
UCSR0B |= (1<<RXEN0 | 1<<TXEN0 | 1<<RXCIE0); UCSR0B |= (1 << RXEN0 | 1 << TXEN0 | 1 << RXCIE0);
// defaults to 8-bit, no parity, 1 stop bit // defaults to 8-bit, no parity, 1 stop bit
} }
// Writes one byte to the TX serial buffer. Called by main program. // Writes one byte to the TX serial buffer. Called by main program.
void serial_write(uint8_t data) { void serial_write(uint8_t data) {
// Calculate next head // Calculate next head
uint8_t next_head = serial_tx_buffer_head + 1; uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_RING_BUFFER) { next_head = 0; } if (next_head == TX_RING_BUFFER) {
next_head = 0;
}
// Wait until there is space in the buffer // Wait until there is space in the buffer
while (next_head == serial_tx_buffer_tail) { while (next_head == serial_tx_buffer_tail) {
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print. // TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
if (sys_rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop. if (sys_rt_exec_state & EXEC_RESET) {
} return;
} // Only check for abort to avoid an endless loop.
}
// Store data and advance head // Store data and advance head
serial_tx_buffer[serial_tx_buffer_head] = data; serial_tx_buffer[serial_tx_buffer_head] = data;
serial_tx_buffer_head = next_head; serial_tx_buffer_head = next_head;
// Enable Data Register Empty Interrupt to make sure tx-streaming is running // Enable Data Register Empty Interrupt to make sure tx-streaming is running
UCSR0B |= (1 << UDRIE0); UCSR0B |= (1 << UDRIE0);
} }
// Data Register Empty Interrupt handler // Data Register Empty Interrupt handler
ISR(SERIAL_UDRE) ISR(SERIAL_UDRE) {
{ uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
// Send a byte from the buffer // Send a byte from the buffer
UDR0 = serial_tx_buffer[tail]; UDR0 = serial_tx_buffer[tail];
// Update tail position // Update tail position
tail++; tail++;
if (tail == TX_RING_BUFFER) { tail = 0; } if (tail == TX_RING_BUFFER) {
tail = 0;
}
serial_tx_buffer_tail = tail; serial_tx_buffer_tail = tail;
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer // Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } if (tail == serial_tx_buffer_head) {
UCSR0B &= ~(1 << UDRIE0);
}
} }
// Fetches the first byte in the serial read buffer. Called by main program. // Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read() uint8_t serial_read() {
{ uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile) if (serial_rx_buffer_head == tail) {
if (serial_rx_buffer_head == tail) { return SERIAL_NO_DATA;
return SERIAL_NO_DATA; } else {
} else { uint8_t data = serial_rx_buffer[tail];
uint8_t data = serial_rx_buffer[tail];
tail++; tail++;
if (tail == RX_RING_BUFFER) { tail = 0; } if (tail == RX_RING_BUFFER) {
serial_rx_buffer_tail = tail; tail = 0;
}
serial_rx_buffer_tail = tail;
return data; return data;
} }
} }
ISR(SERIAL_RX) {
uint8_t data = UDR0;
uint8_t next_head;
ISR(SERIAL_RX) // Pick off realtime command characters directly from the serial stream. These characters are
{ // not passed into the main buffer, but these set system state flag bits for realtime execution.
uint8_t data = UDR0; switch (data) {
uint8_t next_head; case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
// Pick off realtime command characters directly from the serial stream. These characters are
// not passed into the main buffer, but these set system state flag bits for realtime execution.
switch (data) {
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
case CMD_STATUS_REPORT: system_set_exec_state_flag(EXEC_STATUS_REPORT); break; // Set as true case CMD_STATUS_REPORT: system_set_exec_state_flag(EXEC_STATUS_REPORT); break; // Set as true
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
default : default:
if (data > 0x7F) { // Real-time control characters are extended ACSII only. if (data > 0x7F) { // Real-time control characters are extended ACSII only.
switch(data) { switch (data) {
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
case CMD_JOG_CANCEL: case CMD_JOG_CANCEL:
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel. if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
system_set_exec_state_flag(EXEC_MOTION_CANCEL); system_set_exec_state_flag(EXEC_MOTION_CANCEL);
}
break;
#ifdef DEBUG
case CMD_DEBUG_REPORT: {
uint8_t sreg = SREG;
cli();
bit_true(sys_rt_exec_debug, EXEC_DEBUG_REPORT);
SREG = sreg;
} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS);
break;
case CMD_SPINDLE_OVR_COARSE_MINUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS);
break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS);
break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE:
system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE);
break;
#ifdef ENABLE_M7
case CMD_COOLANT_MIST_OVR_TOGGLE:
system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE);
break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_RING_BUFFER) {
next_head = 0;
} }
break;
#ifdef DEBUG
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
#ifdef ENABLE_M7
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
// Write data to buffer unless it is full. // Write data to buffer unless it is full.
if (next_head != serial_rx_buffer_tail) { if (next_head != serial_rx_buffer_tail) {
serial_rx_buffer[serial_rx_buffer_head] = data; serial_rx_buffer[serial_rx_buffer_head] = data;
serial_rx_buffer_head = next_head; serial_rx_buffer_head = next_head;
}
} }
} }
}
} }
void serial_reset_read_buffer() {
void serial_reset_read_buffer() serial_rx_buffer_tail = serial_rx_buffer_head;
{
serial_rx_buffer_tail = serial_rx_buffer_head;
} }

View file

@ -22,21 +22,19 @@
#ifndef serial_h #ifndef serial_h
#define serial_h #define serial_h
#ifndef RX_BUFFER_SIZE #ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128 #define RX_BUFFER_SIZE 128
#endif #endif
#ifndef TX_BUFFER_SIZE #ifndef TX_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
#define TX_BUFFER_SIZE 112 #define TX_BUFFER_SIZE 112
#else #else
#define TX_BUFFER_SIZE 104 #define TX_BUFFER_SIZE 104
#endif #endif
#endif #endif
#define SERIAL_NO_DATA 0xff #define SERIAL_NO_DATA 0xff
void serial_init(); void serial_init();
// Writes one byte to the TX serial buffer. Called by main program. // Writes one byte to the TX serial buffer. Called by main program.

View file

@ -23,7 +23,7 @@
settings_t settings; settings_t settings;
const __flash settings_t defaults = {\ const __flash settings_t defaults = {
.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS, .pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS,
.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME, .stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME,
.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK, .step_invert_mask = DEFAULT_STEPPING_INVERT_MASK,
@ -38,14 +38,10 @@ const __flash settings_t defaults = {\
.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE, .homing_seek_rate = DEFAULT_HOMING_SEEK_RATE,
.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY, .homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY,
.homing_pulloff = DEFAULT_HOMING_PULLOFF, .homing_pulloff = DEFAULT_HOMING_PULLOFF,
.flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | \ .flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | (DEFAULT_LASER_MODE << BIT_LASER_MODE) |
(DEFAULT_LASER_MODE << BIT_LASER_MODE) | \ (DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | (DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) |
(DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | \ (DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | (DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) |
(DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) | \ (DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | (DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN),
(DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | \
(DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) | \
(DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | \
(DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN),
.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM, .steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM,
.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM, .steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM,
.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM, .steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM,
@ -59,282 +55,313 @@ const __flash settings_t defaults = {\
.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL), .max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL),
.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL)}; .max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL)};
// Method to store startup lines into EEPROM // Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char *line) void settings_store_startup_line(uint8_t n, char *line) {
{ #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing. #endif
#endif uint32_t addr = n * (LINE_BUFFER_SIZE + 1) + EEPROM_ADDR_STARTUP_BLOCK;
uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; memcpy_to_eeprom_with_checksum(addr, (char *)line, LINE_BUFFER_SIZE);
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
} }
// Method to store build info into EEPROM // Method to store build info into EEPROM
// NOTE: This function can only be called in IDLE state. // NOTE: This function can only be called in IDLE state.
void settings_store_build_info(char *line) void settings_store_build_info(char *line) {
{ // Build info can only be stored when state is IDLE.
// Build info can only be stored when state is IDLE. memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO, (char *)line, LINE_BUFFER_SIZE);
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE);
} }
// Method to store coord data parameters into EEPROM // Method to store coord data parameters into EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data) void settings_write_coord_data(uint8_t coord_select, float *coord_data) {
{ #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize(); protocol_buffer_synchronize();
#endif #endif
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; uint32_t addr = coord_select * (sizeof(float) * N_AXIS + 1) + EEPROM_ADDR_PARAMETERS;
memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); memcpy_to_eeprom_with_checksum(addr, (char *)coord_data, sizeof(float) * N_AXIS);
} }
// Method to store Grbl global settings struct and version number into EEPROM // Method to store Grbl global settings struct and version number into EEPROM
// NOTE: This function can only be called in IDLE state. // NOTE: This function can only be called in IDLE state.
void write_global_settings() void write_global_settings() {
{ eeprom_put_char(0, SETTINGS_VERSION);
eeprom_put_char(0, SETTINGS_VERSION); memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char *)&settings, sizeof(settings_t));
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
} }
// Method to restore EEPROM-saved Grbl global settings back to defaults. // Method to restore EEPROM-saved Grbl global settings back to defaults.
void settings_restore(uint8_t restore_flag) { void settings_restore(uint8_t restore_flag) {
if (restore_flag & SETTINGS_RESTORE_DEFAULTS) { if (restore_flag & SETTINGS_RESTORE_DEFAULTS) {
settings = defaults; settings = defaults;
write_global_settings(); write_global_settings();
} }
if (restore_flag & SETTINGS_RESTORE_PARAMETERS) { if (restore_flag & SETTINGS_RESTORE_PARAMETERS) {
uint8_t idx; uint8_t idx;
float coord_data[N_AXIS]; float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data)); memset(&coord_data, 0, sizeof(coord_data));
for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); } for (idx = 0; idx <= SETTING_INDEX_NCOORD; idx++) {
} settings_write_coord_data(idx, coord_data);
}
}
if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) { if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) {
#if N_STARTUP_LINE > 0 #if N_STARTUP_LINE > 0
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0); eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0);
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+1, 0); // Checksum eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK + 1, 0); // Checksum
#endif #endif
#if N_STARTUP_LINE > 1 #if N_STARTUP_LINE > 1
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0); eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK + (LINE_BUFFER_SIZE + 1), 0);
eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+2), 0); // Checksum eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK + (LINE_BUFFER_SIZE + 2), 0); // Checksum
#endif #endif
} }
if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) { if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) {
eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); eeprom_put_char(EEPROM_ADDR_BUILD_INFO, 0);
eeprom_put_char(EEPROM_ADDR_BUILD_INFO+1 , 0); // Checksum eeprom_put_char(EEPROM_ADDR_BUILD_INFO + 1, 0); // Checksum
} }
} }
// Reads startup line from EEPROM. Updated pointed line string data. // Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_startup_line(uint8_t n, char *line) uint8_t settings_read_startup_line(uint8_t n, char *line) {
{ uint32_t addr = n * (LINE_BUFFER_SIZE + 1) + EEPROM_ADDR_STARTUP_BLOCK;
uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; if (!(memcpy_from_eeprom_with_checksum((char *)line, addr, LINE_BUFFER_SIZE))) {
if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { // Reset line with default value
// Reset line with default value line[0] = 0; // Empty line
line[0] = 0; // Empty line settings_store_startup_line(n, line);
settings_store_startup_line(n, line); return (false);
return(false); }
} return (true);
return(true);
} }
// Reads startup line from EEPROM. Updated pointed line string data. // Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_build_info(char *line) uint8_t settings_read_build_info(char *line) {
{ if (!(memcpy_from_eeprom_with_checksum((char *)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) {
if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) { // Reset line with default value
// Reset line with default value line[0] = 0; // Empty line
line[0] = 0; // Empty line settings_store_build_info(line);
settings_store_build_info(line); return (false);
return(false); }
} return (true);
return(true);
} }
// Read selected coordinate data from EEPROM. Updates pointed coord_data value. // Read selected coordinate data from EEPROM. Updates pointed coord_data value.
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) {
{ uint32_t addr = coord_select * (sizeof(float) * N_AXIS + 1) + EEPROM_ADDR_PARAMETERS;
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; if (!(memcpy_from_eeprom_with_checksum((char *)coord_data, addr, sizeof(float) * N_AXIS))) {
if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { // Reset with default zero vector
// Reset with default zero vector clear_vector_float(coord_data);
clear_vector_float(coord_data); settings_write_coord_data(coord_select, coord_data);
settings_write_coord_data(coord_select,coord_data); return (false);
return(false); }
} return (true);
return(true);
} }
// Reads Grbl global settings struct from EEPROM. // Reads Grbl global settings struct from EEPROM.
uint8_t read_global_settings() { uint8_t read_global_settings() {
// Check version-byte of eeprom // Check version-byte of eeprom
uint8_t version = eeprom_get_char(0); uint8_t version = eeprom_get_char(0);
if (version == SETTINGS_VERSION) { if (version == SETTINGS_VERSION) {
// Read settings-record and check checksum // Read settings-record and check checksum
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { if (!(memcpy_from_eeprom_with_checksum((char *)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) {
return(false); return (false);
}
} else {
return (false);
} }
} else { return (true);
return(false);
}
return(true);
} }
// A helper method to set settings from command line // A helper method to set settings from command line
uint8_t settings_store_global_setting(uint8_t parameter, float value) { uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); } if (value < 0.0) {
if (parameter >= AXIS_SETTINGS_START_VAL) { return (STATUS_NEGATIVE_VALUE);
// Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
// NOTE: Ensure the setting index corresponds to the report.c settings printout.
parameter -= AXIS_SETTINGS_START_VAL;
uint8_t set_idx = 0;
while (set_idx < AXIS_N_SETTINGS) {
if (parameter < N_AXIS) {
// Valid axis setting found.
switch (set_idx) {
case 0:
#ifdef MAX_STEP_RATE_HZ
if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
#endif
settings.steps_per_mm[parameter] = value;
break;
case 1:
#ifdef MAX_STEP_RATE_HZ
if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
#endif
settings.max_rate[parameter] = value;
break;
case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
}
break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
} else {
set_idx++;
// If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); }
parameter -= AXIS_SETTINGS_INCREMENT;
}
} }
} else { if (parameter >= AXIS_SETTINGS_START_VAL) {
// Store non-axis Grbl settings // Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
uint8_t int_value = trunc(value); // NOTE: Ensure the setting index corresponds to the report.c settings printout.
switch(parameter) { parameter -= AXIS_SETTINGS_START_VAL;
case 0: uint8_t set_idx = 0;
if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } while (set_idx < AXIS_N_SETTINGS) {
settings.pulse_microseconds = int_value; break; if (parameter < N_AXIS) {
case 1: settings.stepper_idle_lock_time = int_value; break; // Valid axis setting found.
case 2: switch (set_idx) {
settings.step_invert_mask = int_value; case 0:
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. #ifdef MAX_STEP_RATE_HZ
break; if (value * settings.max_rate[parameter] > (MAX_STEP_RATE_HZ * 60.0)) {
case 3: return (STATUS_MAX_STEP_RATE_EXCEEDED);
settings.dir_invert_mask = int_value; }
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. #endif
break; settings.steps_per_mm[parameter] = value;
case 4: // Reset to ensure change. Immediate re-init may cause problems. break;
if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } case 1:
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } #ifdef MAX_STEP_RATE_HZ
break; if (value * settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ * 60.0)) {
case 5: // Reset to ensure change. Immediate re-init may cause problems. return (STATUS_MAX_STEP_RATE_EXCEEDED);
if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } }
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; } #endif
break; settings.max_rate[parameter] = value;
case 6: // Reset to ensure change. Immediate re-init may cause problems. break;
if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } case 2:
else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; } settings.acceleration[parameter] = value * 60 * 60;
probe_configure_invert_mask(false); break; // Convert to mm/min^2 for grbl internal use.
break; case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
case 10: settings.status_report_mask = int_value; break; }
case 11: settings.junction_deviation = value; break; break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
case 12: settings.arc_tolerance = value; break; } else {
case 13: set_idx++;
if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; } // If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
else { settings.flags &= ~BITFLAG_REPORT_INCHES; } if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) {
system_flag_wco_change(); // Make sure WCO is immediately updated. return (STATUS_INVALID_STATEMENT);
break; }
case 20: parameter -= AXIS_SETTINGS_INCREMENT;
if (int_value) { }
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); } }
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } else {
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; } // Store non-axis Grbl settings
break; uint8_t int_value = trunc(value);
case 21: switch (parameter) {
if (int_value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } case 0:
else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } if (int_value < 3) {
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. return (STATUS_SETTING_STEP_PULSE_MIN);
break; }
case 22: settings.pulse_microseconds = int_value;
if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; } break;
else { case 1: settings.stepper_idle_lock_time = int_value; break;
settings.flags &= ~BITFLAG_HOMING_ENABLE; case 2:
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits. settings.step_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 3:
settings.dir_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 4: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) {
settings.flags |= BITFLAG_INVERT_ST_ENABLE;
} else {
settings.flags &= ~BITFLAG_INVERT_ST_ENABLE;
}
break;
case 5: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) {
settings.flags |= BITFLAG_INVERT_LIMIT_PINS;
} else {
settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS;
}
break;
case 6: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) {
settings.flags |= BITFLAG_INVERT_PROBE_PIN;
} else {
settings.flags &= ~BITFLAG_INVERT_PROBE_PIN;
}
probe_configure_invert_mask(false);
break;
case 10: settings.status_report_mask = int_value; break;
case 11: settings.junction_deviation = value; break;
case 12: settings.arc_tolerance = value; break;
case 13:
if (int_value) {
settings.flags |= BITFLAG_REPORT_INCHES;
} else {
settings.flags &= ~BITFLAG_REPORT_INCHES;
}
system_flag_wco_change(); // Make sure WCO is immediately updated.
break;
case 20:
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) {
return (STATUS_SOFT_LIMIT_ERROR);
}
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else {
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE;
}
break;
case 21:
if (int_value) {
settings.flags |= BITFLAG_HARD_LIMIT_ENABLE;
} else {
settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE;
}
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
break;
case 22:
if (int_value) {
settings.flags |= BITFLAG_HOMING_ENABLE;
} else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
}
break;
case 23: settings.homing_dir_mask = int_value; break;
case 24: settings.homing_feed_rate = value; break;
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
case 30:
settings.rpm_max = value;
spindle_init();
break; // Re-initialize spindle rpm calibration
case 31:
settings.rpm_min = value;
spindle_init();
break; // Re-initialize spindle rpm calibration
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) {
settings.flags |= BITFLAG_LASER_MODE;
} else {
settings.flags &= ~BITFLAG_LASER_MODE;
}
#else
return (STATUS_SETTING_DISABLED_LASER);
#endif
break;
default: return (STATUS_INVALID_STATEMENT);
} }
break;
case 23: settings.homing_dir_mask = int_value; break;
case 24: settings.homing_feed_rate = value; break;
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }
else { settings.flags &= ~BITFLAG_LASER_MODE; }
#else
return(STATUS_SETTING_DISABLED_LASER);
#endif
break;
default:
return(STATUS_INVALID_STATEMENT);
} }
} write_global_settings();
write_global_settings(); return (STATUS_OK);
return(STATUS_OK);
} }
// Initialize the config subsystem // Initialize the config subsystem
void settings_init() { void settings_init() {
if(!read_global_settings()) { if (!read_global_settings()) {
report_status_message(STATUS_SETTING_READ_FAIL); report_status_message(STATUS_SETTING_READ_FAIL);
settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data. settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data.
report_grbl_settings(); report_grbl_settings();
} }
} }
// Returns step pin mask according to Grbl internal axis indexing. // Returns step pin mask according to Grbl internal axis indexing.
uint8_t get_step_pin_mask(uint8_t axis_idx) uint8_t get_step_pin_mask(uint8_t axis_idx) {
{ if (axis_idx == X_AXIS) {
if ( axis_idx == X_AXIS ) { return((1<<X_STEP_BIT)); } return ((1 << X_STEP_BIT));
if ( axis_idx == Y_AXIS ) { return((1<<Y_STEP_BIT)); } }
return((1<<Z_STEP_BIT)); if (axis_idx == Y_AXIS) {
return ((1 << Y_STEP_BIT));
}
return ((1 << Z_STEP_BIT));
} }
// Returns direction pin mask according to Grbl internal axis indexing. // Returns direction pin mask according to Grbl internal axis indexing.
uint8_t get_direction_pin_mask(uint8_t axis_idx) uint8_t get_direction_pin_mask(uint8_t axis_idx) {
{ if (axis_idx == X_AXIS) {
if ( axis_idx == X_AXIS ) { return((1<<X_DIRECTION_BIT)); } return ((1 << X_DIRECTION_BIT));
if ( axis_idx == Y_AXIS ) { return((1<<Y_DIRECTION_BIT)); } }
return((1<<Z_DIRECTION_BIT)); if (axis_idx == Y_AXIS) {
return ((1 << Y_DIRECTION_BIT));
}
return ((1 << Z_DIRECTION_BIT));
} }
// Returns limit pin mask according to Grbl internal axis indexing. // Returns limit pin mask according to Grbl internal axis indexing.
uint8_t get_limit_pin_mask(uint8_t axis_idx) uint8_t get_limit_pin_mask(uint8_t axis_idx) {
{ if (axis_idx == X_AXIS) {
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); } return ((1 << X_LIMIT_BIT));
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); } }
return((1<<Z_LIMIT_BIT)); if (axis_idx == Y_AXIS) {
return ((1 << Y_LIMIT_BIT));
}
return ((1 << Z_LIMIT_BIT));
} }

View file

@ -24,93 +24,93 @@
#include "grbl.h" #include "grbl.h"
// 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 10 // 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 BIT_REPORT_INCHES 0 #define BIT_REPORT_INCHES 0
#define BIT_LASER_MODE 1 #define BIT_LASER_MODE 1
#define BIT_INVERT_ST_ENABLE 2 #define BIT_INVERT_ST_ENABLE 2
#define BIT_HARD_LIMIT_ENABLE 3 #define BIT_HARD_LIMIT_ENABLE 3
#define BIT_HOMING_ENABLE 4 #define BIT_HOMING_ENABLE 4
#define BIT_SOFT_LIMIT_ENABLE 5 #define BIT_SOFT_LIMIT_ENABLE 5
#define BIT_INVERT_LIMIT_PINS 6 #define BIT_INVERT_LIMIT_PINS 6
#define BIT_INVERT_PROBE_PIN 7 #define BIT_INVERT_PROBE_PIN 7
#define BITFLAG_REPORT_INCHES bit(BIT_REPORT_INCHES) #define BITFLAG_REPORT_INCHES bit(BIT_REPORT_INCHES)
#define BITFLAG_LASER_MODE bit(BIT_LASER_MODE) #define BITFLAG_LASER_MODE bit(BIT_LASER_MODE)
#define BITFLAG_INVERT_ST_ENABLE bit(BIT_INVERT_ST_ENABLE) #define BITFLAG_INVERT_ST_ENABLE bit(BIT_INVERT_ST_ENABLE)
#define BITFLAG_HARD_LIMIT_ENABLE bit(BIT_HARD_LIMIT_ENABLE) #define BITFLAG_HARD_LIMIT_ENABLE bit(BIT_HARD_LIMIT_ENABLE)
#define BITFLAG_HOMING_ENABLE bit(BIT_HOMING_ENABLE) #define BITFLAG_HOMING_ENABLE bit(BIT_HOMING_ENABLE)
#define BITFLAG_SOFT_LIMIT_ENABLE bit(BIT_SOFT_LIMIT_ENABLE) #define BITFLAG_SOFT_LIMIT_ENABLE bit(BIT_SOFT_LIMIT_ENABLE)
#define BITFLAG_INVERT_LIMIT_PINS bit(BIT_INVERT_LIMIT_PINS) #define BITFLAG_INVERT_LIMIT_PINS bit(BIT_INVERT_LIMIT_PINS)
#define BITFLAG_INVERT_PROBE_PIN bit(BIT_INVERT_PROBE_PIN) #define BITFLAG_INVERT_PROBE_PIN bit(BIT_INVERT_PROBE_PIN)
// Define status reporting boolean enable bit flags in settings.status_report_mask // Define status reporting boolean enable bit flags in settings.status_report_mask
#define BITFLAG_RT_STATUS_POSITION_TYPE bit(0) #define BITFLAG_RT_STATUS_POSITION_TYPE bit(0)
#define BITFLAG_RT_STATUS_BUFFER_STATE bit(1) #define BITFLAG_RT_STATUS_BUFFER_STATE bit(1)
// Define settings restore bitflags. // Define settings restore bitflags.
#define SETTINGS_RESTORE_DEFAULTS bit(0) #define SETTINGS_RESTORE_DEFAULTS bit(0)
#define SETTINGS_RESTORE_PARAMETERS bit(1) #define SETTINGS_RESTORE_PARAMETERS bit(1)
#define SETTINGS_RESTORE_STARTUP_LINES bit(2) #define SETTINGS_RESTORE_STARTUP_LINES bit(2)
#define SETTINGS_RESTORE_BUILD_INFO bit(3) #define SETTINGS_RESTORE_BUILD_INFO bit(3)
#ifndef SETTINGS_RESTORE_ALL #ifndef SETTINGS_RESTORE_ALL
#define SETTINGS_RESTORE_ALL 0xFF // All bitflags #define SETTINGS_RESTORE_ALL 0xFF // All bitflags
#endif #endif
// Define EEPROM memory address location values for Grbl settings and parameters // Define EEPROM memory address location values for Grbl settings and parameters
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and // NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
// the startup script. The lower half contains the global settings and space for future // the startup script. The lower half contains the global settings and space for future
// developments. // developments.
#define EEPROM_ADDR_GLOBAL 1U #define EEPROM_ADDR_GLOBAL 1U
#define EEPROM_ADDR_PARAMETERS 512U #define EEPROM_ADDR_PARAMETERS 512U
#define EEPROM_ADDR_STARTUP_BLOCK 768U #define EEPROM_ADDR_STARTUP_BLOCK 768U
#define EEPROM_ADDR_BUILD_INFO 942U #define EEPROM_ADDR_BUILD_INFO 942U
// Define EEPROM address indexing for coordinate parameters // Define EEPROM address indexing for coordinate parameters
#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1)
#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0) #define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM + 1 // Total number of system stored (from index 0)
// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) // NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59)
#define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1 #define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1
#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2 #define SETTING_INDEX_G30 N_COORDINATE_SYSTEM + 1 // Home position 2
// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported) // #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported)
// Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS. // Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS.
#define AXIS_N_SETTINGS 4 #define AXIS_N_SETTINGS 4
#define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255. #define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255.
#define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings #define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) // Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
typedef struct { typedef struct {
// Axis settings // Axis settings
float steps_per_mm[N_AXIS]; float steps_per_mm[N_AXIS];
float max_rate[N_AXIS]; float max_rate[N_AXIS];
float acceleration[N_AXIS]; float acceleration[N_AXIS];
float max_travel[N_AXIS]; float max_travel[N_AXIS];
// Remaining Grbl settings // Remaining Grbl settings
uint8_t pulse_microseconds; uint8_t pulse_microseconds;
uint8_t step_invert_mask; uint8_t step_invert_mask;
uint8_t dir_invert_mask; uint8_t dir_invert_mask;
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
uint8_t status_report_mask; // Mask to indicate desired report data. uint8_t status_report_mask; // Mask to indicate desired report data.
float junction_deviation; float junction_deviation;
float arc_tolerance; float arc_tolerance;
float rpm_max; float rpm_max;
float rpm_min; 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;
float homing_feed_rate; float homing_feed_rate;
float homing_seek_rate; float homing_seek_rate;
uint16_t homing_debounce_delay; uint16_t homing_debounce_delay;
float homing_pulloff; float homing_pulloff;
} settings_t; } settings_t;
extern settings_t settings; extern settings_t settings;
// Initialize the configuration subsystem (load settings from EEPROM) // Initialize the configuration subsystem (load settings from EEPROM)
@ -149,5 +149,4 @@ uint8_t get_direction_pin_mask(uint8_t i);
// Returns the limit pin mask according to Grbl's internal axis numbering // Returns the limit pin mask according to Grbl's internal axis numbering
uint8_t get_limit_pin_mask(uint8_t i); uint8_t get_limit_pin_mask(uint8_t i);
#endif #endif

View file

@ -21,270 +21,273 @@
#include "grbl.h" #include "grbl.h"
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions. static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions.
#endif #endif
void spindle_init() {
void spindle_init() #ifdef VARIABLE_SPINDLE
{
#ifdef VARIABLE_SPINDLE
// Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are // Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
// combined unless configured otherwise. // 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.
SPINDLE_TCCRA_REGISTER = SPINDLE_TCCRA_INIT_MASK; // Configure PWM output compare timer SPINDLE_TCCRA_REGISTER = SPINDLE_TCCRA_INIT_MASK; // Configure PWM output compare timer
SPINDLE_TCCRB_REGISTER = SPINDLE_TCCRB_INIT_MASK; SPINDLE_TCCRB_REGISTER = SPINDLE_TCCRB_INIT_MASK;
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin. SPINDLE_ENABLE_DDR |= (1 << SPINDLE_ENABLE_BIT); // Configure as output pin.
#else #else
#ifndef ENABLE_DUAL_AXIS #ifndef ENABLE_DUAL_AXIS
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin. SPINDLE_DIRECTION_DDR |= (1 << SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif #endif
#endif #endif
pwm_gradient = SPINDLE_PWM_RANGE/(settings.rpm_max-settings.rpm_min); pwm_gradient = SPINDLE_PWM_RANGE / (settings.rpm_max - settings.rpm_min);
#else #else
SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin. SPINDLE_ENABLE_DDR |= (1 << SPINDLE_ENABLE_BIT); // Configure as output pin.
#ifndef ENABLE_DUAL_AXIS #ifndef ENABLE_DUAL_AXIS
SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin. SPINDLE_DIRECTION_DDR |= (1 << SPINDLE_DIRECTION_BIT); // Configure as output pin.
#endif #endif
#endif #endif
spindle_stop(); spindle_stop();
} }
uint8_t spindle_get_state() {
uint8_t spindle_get_state() #ifdef VARIABLE_SPINDLE
{ #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
#ifdef VARIABLE_SPINDLE // No spindle direction output pin.
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN #ifdef INVERT_SPINDLE_ENABLE_PIN
// No spindle direction output pin. if (bit_isfalse(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
#ifdef INVERT_SPINDLE_ENABLE_PIN return (SPINDLE_STATE_CW);
if (bit_isfalse(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) { return(SPINDLE_STATE_CW); }
#else
if (bit_istrue(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) { return(SPINDLE_STATE_CW); }
#endif
#else
if (SPINDLE_TCCRA_REGISTER & (1<<SPINDLE_COMB_BIT)) { // Check if PWM is enabled.
#ifdef ENABLE_DUAL_AXIS
return(SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1<<SPINDLE_DIRECTION_BIT)) { return(SPINDLE_STATE_CCW); }
else { return(SPINDLE_STATE_CW); }
#endif
}
#endif
#else
#ifdef INVERT_SPINDLE_ENABLE_PIN
if (bit_isfalse(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) {
#else
if (bit_istrue(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) {
#endif
#ifdef ENABLE_DUAL_AXIS
return(SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1<<SPINDLE_DIRECTION_BIT)) { return(SPINDLE_STATE_CCW); }
else { return(SPINDLE_STATE_CW); }
#endif
} }
#endif #else
return(SPINDLE_STATE_DISABLE); if (bit_istrue(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
return (SPINDLE_STATE_CW);
}
#endif
#else
if (SPINDLE_TCCRA_REGISTER & (1 << SPINDLE_COMB_BIT)) { // Check if PWM is enabled.
#ifdef ENABLE_DUAL_AXIS
return (SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1 << SPINDLE_DIRECTION_BIT)) {
return (SPINDLE_STATE_CCW);
} else {
return (SPINDLE_STATE_CW);
}
#endif
}
#endif
#else
#ifdef INVERT_SPINDLE_ENABLE_PIN
if (bit_isfalse(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
#else
if (bit_istrue(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
#endif
#ifdef ENABLE_DUAL_AXIS
return (SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1 << SPINDLE_DIRECTION_BIT)) {
return (SPINDLE_STATE_CCW);
} else {
return (SPINDLE_STATE_CW);
}
#endif
}
#endif
return (SPINDLE_STATE_DISABLE);
} }
// Disables the spindle and sets PWM output to zero when PWM variable spindle speed is enabled. // Disables the spindle and sets PWM output to zero when PWM variable spindle speed is enabled.
// Called by various main program and ISR routines. Keep routine small, fast, and efficient. // Called by various main program and ISR routines. Keep routine small, fast, and efficient.
// Called by spindle_init(), spindle_set_speed(), spindle_set_state(), and mc_reset(). // Called by spindle_init(), spindle_set_speed(), spindle_set_state(), and mc_reset().
void spindle_stop() void spindle_stop() {
{ #ifdef VARIABLE_SPINDLE
#ifdef VARIABLE_SPINDLE SPINDLE_TCCRA_REGISTER &= ~(1 << SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero.
SPINDLE_TCCRA_REGISTER &= ~(1<<SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero. #ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
#ifdef 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
} }
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Sets spindle speed PWM output and enable pin, if configured. Called by spindle_set_state() // Sets spindle speed PWM output and enable pin, if configured. Called by spindle_set_state()
// and stepper ISR. Keep routine small and efficient. // and stepper ISR. Keep routine small and efficient.
void spindle_set_speed(uint8_t pwm_value) void spindle_set_speed(uint8_t pwm_value) {
{
SPINDLE_OCR_REGISTER = pwm_value; // Set PWM output level. SPINDLE_OCR_REGISTER = pwm_value; // Set PWM output level.
#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED #ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED
if (pwm_value == SPINDLE_PWM_OFF_VALUE) { if (pwm_value == SPINDLE_PWM_OFF_VALUE) {
spindle_stop(); spindle_stop();
} else { } else {
SPINDLE_TCCRA_REGISTER |= (1<<SPINDLE_COMB_BIT); // Ensure PWM output is enabled. SPINDLE_TCCRA_REGISTER |= (1 << SPINDLE_COMB_BIT); // Ensure PWM output is enabled.
#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
} }
#else #else
if (pwm_value == SPINDLE_PWM_OFF_VALUE) { if (pwm_value == SPINDLE_PWM_OFF_VALUE) {
SPINDLE_TCCRA_REGISTER &= ~(1<<SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero. SPINDLE_TCCRA_REGISTER &= ~(1 << SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero.
} else { } else {
SPINDLE_TCCRA_REGISTER |= (1<<SPINDLE_COMB_BIT); // Ensure PWM output is enabled. SPINDLE_TCCRA_REGISTER |= (1 << SPINDLE_COMB_BIT); // Ensure PWM output is enabled.
} }
#endif #endif
} }
#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE
#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE // Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit.
// Called by spindle_set_state() and step segment generator. Keep routine small and efficient. {
uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit. uint8_t pwm_value;
{ rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value.
uint8_t pwm_value; // Calculate PWM register value based on rpm max/min settings and programmed rpm.
rpm *= (0.010*sys.spindle_speed_ovr); // Scale by spindle speed override value. if ((settings.rpm_min >= settings.rpm_max) || (rpm >= RPM_MAX)) {
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ((settings.rpm_min >= settings.rpm_max) || (rpm >= RPM_MAX)) {
rpm = RPM_MAX; rpm = RPM_MAX;
pwm_value = SPINDLE_PWM_MAX_VALUE; pwm_value = SPINDLE_PWM_MAX_VALUE;
} else if (rpm <= RPM_MIN) { } else if (rpm <= RPM_MIN) {
if (rpm == 0.0) { // S0 disables spindle if (rpm == 0.0) { // S0 disables spindle
pwm_value = SPINDLE_PWM_OFF_VALUE; pwm_value = SPINDLE_PWM_OFF_VALUE;
} else { } else {
rpm = RPM_MIN; rpm = RPM_MIN;
pwm_value = SPINDLE_PWM_MIN_VALUE; pwm_value = SPINDLE_PWM_MIN_VALUE;
} }
} else { } else {
// Compute intermediate PWM value with linear spindle speed model via piecewise linear fit model. // Compute intermediate PWM value with linear spindle speed model via piecewise linear fit model.
#if (N_PIECES > 3) #if (N_PIECES > 3)
if (rpm > RPM_POINT34) { if (rpm > RPM_POINT34) {
pwm_value = floor(RPM_LINE_A4*rpm - RPM_LINE_B4); pwm_value = floor(RPM_LINE_A4 * rpm - RPM_LINE_B4);
} else } else
#endif #endif
#if (N_PIECES > 2) #if (N_PIECES > 2)
if (rpm > RPM_POINT23) { if (rpm > RPM_POINT23) {
pwm_value = floor(RPM_LINE_A3*rpm - RPM_LINE_B3); pwm_value = floor(RPM_LINE_A3 * rpm - RPM_LINE_B3);
} else } else
#endif #endif
#if (N_PIECES > 1) #if (N_PIECES > 1)
if (rpm > RPM_POINT12) { if (rpm > RPM_POINT12) {
pwm_value = floor(RPM_LINE_A2*rpm - RPM_LINE_B2); pwm_value = floor(RPM_LINE_A2 * rpm - RPM_LINE_B2);
} else } else
#endif #endif
{ {
pwm_value = floor(RPM_LINE_A1*rpm - RPM_LINE_B1); pwm_value = floor(RPM_LINE_A1 * rpm - RPM_LINE_B1);
} }
}
sys.spindle_speed = rpm;
return(pwm_value);
} }
sys.spindle_speed = rpm;
#else return (pwm_value);
}
// Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit. #else
{
uint8_t pwm_value; // Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
rpm *= (0.010*sys.spindle_speed_ovr); // Scale by spindle speed override value. uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit.
// Calculate PWM register value based on rpm max/min settings and programmed rpm. {
if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) { uint8_t pwm_value;
rpm *= (0.010 * sys.spindle_speed_ovr); // Scale by spindle speed override value.
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) {
// No PWM range possible. Set simple on/off spindle control pin state. // No PWM range possible. Set simple on/off spindle control pin state.
sys.spindle_speed = settings.rpm_max; sys.spindle_speed = settings.rpm_max;
pwm_value = SPINDLE_PWM_MAX_VALUE; pwm_value = SPINDLE_PWM_MAX_VALUE;
} else if (rpm <= settings.rpm_min) { } else if (rpm <= settings.rpm_min) {
if (rpm == 0.0) { // S0 disables spindle if (rpm == 0.0) { // S0 disables spindle
sys.spindle_speed = 0.0; sys.spindle_speed = 0.0;
pwm_value = SPINDLE_PWM_OFF_VALUE; pwm_value = SPINDLE_PWM_OFF_VALUE;
} else { // Set minimum PWM output } else { // Set minimum PWM output
sys.spindle_speed = settings.rpm_min; sys.spindle_speed = settings.rpm_min;
pwm_value = SPINDLE_PWM_MIN_VALUE; pwm_value = SPINDLE_PWM_MIN_VALUE;
} }
} else { } else {
// Compute intermediate PWM value with linear spindle speed model. // Compute intermediate PWM value with linear spindle speed model.
// NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight.
sys.spindle_speed = rpm; sys.spindle_speed = rpm;
pwm_value = floor((rpm-settings.rpm_min)*pwm_gradient) + SPINDLE_PWM_MIN_VALUE; pwm_value = floor((rpm - settings.rpm_min) * pwm_gradient) + SPINDLE_PWM_MIN_VALUE;
}
return(pwm_value);
} }
return (pwm_value);
#endif }
#endif
#endif
#endif
// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled. // Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
// Called by g-code parser spindle_sync(), parking retract and restore, g-code program end, // Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
// sleep, and spindle stop override. // sleep, and spindle stop override.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
void spindle_set_state(uint8_t state, float rpm) void spindle_set_state(uint8_t state, float rpm)
#else #else
void _spindle_set_state(uint8_t state) void _spindle_set_state(uint8_t state)
#endif #endif
{ {
if (sys.abort) { return; } // Block during abort. if (sys.abort) {
return;
} // Block during abort.
if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm. if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
sys.spindle_speed = 0.0; sys.spindle_speed = 0.0;
#endif #endif
spindle_stop(); spindle_stop();
} else { } else {
#if !defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(ENABLE_DUAL_AXIS) #if !defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(ENABLE_DUAL_AXIS)
if (state == SPINDLE_ENABLE_CW) { if (state == SPINDLE_ENABLE_CW) {
SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT); SPINDLE_DIRECTION_PORT &= ~(1 << SPINDLE_DIRECTION_BIT);
} else { } else {
SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT); SPINDLE_DIRECTION_PORT |= (1 << SPINDLE_DIRECTION_BIT);
} }
#endif #endif
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off. // NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off.
if (settings.flags & BITFLAG_LASER_MODE) { if (settings.flags & BITFLAG_LASER_MODE) {
if (state == SPINDLE_ENABLE_CCW) { rpm = 0.0; } // TODO: May need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE); if (state == SPINDLE_ENABLE_CCW) {
} rpm = 0.0;
spindle_set_speed(spindle_compute_pwm_value(rpm)); } // TODO: May need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE);
#endif }
#if (defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && \ spindle_set_speed(spindle_compute_pwm_value(rpm));
!defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)) || !defined(VARIABLE_SPINDLE) #endif
// NOTE: Without variable spindle, the enable bit should just turn on or off, regardless #if (defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)) || \
// if the spindle speed value is zero, as its ignored anyhow. !defined(VARIABLE_SPINDLE)
#ifdef INVERT_SPINDLE_ENABLE_PIN // NOTE: Without variable spindle, the enable bit should just turn on or off, regardless
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // if the spindle speed value is zero, as its ignored anyhow.
#else #ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); SPINDLE_ENABLE_PORT &= ~(1 << SPINDLE_ENABLE_BIT);
#endif #else
#endif SPINDLE_ENABLE_PORT |= (1 << SPINDLE_ENABLE_BIT);
#endif
} #endif
}
sys.report_ovr_counter = 0; // Set to report change immediately
sys.report_ovr_counter = 0; // Set to report change immediately
} }
// G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails
// G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails
// if an abort or check-mode is active. // if an abort or check-mode is active.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
void spindle_sync(uint8_t state, float rpm) void spindle_sync(uint8_t state, float rpm) {
{ if (sys.state == STATE_CHECK_MODE) {
if (sys.state == STATE_CHECK_MODE) { return; } return;
}
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed. protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
spindle_set_state(state,rpm); spindle_set_state(state, rpm);
} }
#else #else
void _spindle_sync(uint8_t state) void _spindle_sync(uint8_t state) {
{ if (sys.state == STATE_CHECK_MODE) {
if (sys.state == STATE_CHECK_MODE) { return; } return;
}
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed. protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
_spindle_set_state(state); _spindle_set_state(state);
} }
#endif #endif

View file

@ -22,13 +22,12 @@
#ifndef spindle_control_h #ifndef spindle_control_h
#define spindle_control_h #define spindle_control_h
#define SPINDLE_NO_SYNC false #define SPINDLE_NO_SYNC false
#define SPINDLE_FORCE_SYNC true #define SPINDLE_FORCE_SYNC true
#define SPINDLE_STATE_DISABLE 0 // Must be zero. #define SPINDLE_STATE_DISABLE 0 // Must be zero.
#define SPINDLE_STATE_CW bit(0) #define SPINDLE_STATE_CW bit(0)
#define SPINDLE_STATE_CCW bit(1) #define SPINDLE_STATE_CCW bit(1)
// Initializes spindle pins and hardware PWM, if enabled. // Initializes spindle pins and hardware PWM, if enabled.
void spindle_init(); void spindle_init();
@ -41,33 +40,32 @@ uint8_t spindle_get_state();
// Called by spindle_sync() after sync and parking motion/spindle stop override during restore. // Called by spindle_sync() after sync and parking motion/spindle stop override during restore.
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
// Called by g-code parser when setting spindle state and requires a buffer sync. // Called by g-code parser when setting spindle state and requires a buffer sync.
void spindle_sync(uint8_t state, float rpm); void spindle_sync(uint8_t state, float rpm);
// Sets spindle running state with direction, enable, and spindle PWM.
void spindle_set_state(uint8_t state, float rpm);
// Sets spindle PWM quickly for stepper ISR. Also called by spindle_set_state().
// NOTE: 328p PWM register is 8-bit.
void spindle_set_speed(uint8_t pwm_value);
// Computes 328p-specific PWM register value for the given RPM for quick updating.
uint8_t spindle_compute_pwm_value(float rpm);
// Sets spindle running state with direction, enable, and spindle PWM.
void spindle_set_state(uint8_t state, float rpm);
// Sets spindle PWM quickly for stepper ISR. Also called by spindle_set_state().
// NOTE: 328p PWM register is 8-bit.
void spindle_set_speed(uint8_t pwm_value);
// Computes 328p-specific PWM register value for the given RPM for quick updating.
uint8_t spindle_compute_pwm_value(float rpm);
#else #else
// Called by g-code parser when setting spindle state and requires a buffer sync.
#define spindle_sync(state, rpm) _spindle_sync(state)
void _spindle_sync(uint8_t state);
// Sets spindle running state with direction and enable. // Called by g-code parser when setting spindle state and requires a buffer sync.
#define spindle_set_state(state, rpm) _spindle_set_state(state) #define spindle_sync(state, rpm) _spindle_sync(state)
void _spindle_set_state(uint8_t state); void _spindle_sync(uint8_t state);
// Sets spindle running state with direction and enable.
#define spindle_set_state(state, rpm) _spindle_set_state(state)
void _spindle_set_state(uint8_t state);
#endif #endif
// Stop and start spindle routines. Called by all spindle routines and stepper ISR. // Stop and start spindle routines. Called by all spindle routines and stepper ISR.
void spindle_stop(); void spindle_stop();
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,7 @@
#define stepper_h #define stepper_h
#ifndef SEGMENT_BUFFER_SIZE #ifndef SEGMENT_BUFFER_SIZE
#define SEGMENT_BUFFER_SIZE 6 #define SEGMENT_BUFFER_SIZE 6
#endif #endif
// Initialize and setup the stepper motor subsystem // Initialize and setup the stepper motor subsystem

View file

@ -20,98 +20,95 @@
#include "grbl.h" #include "grbl.h"
void system_init() {
void system_init() CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins
{ #ifdef DISABLE_CONTROL_PIN_PULL_UP
CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins
#ifdef DISABLE_CONTROL_PIN_PULL_UP
CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down. CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down.
#else #else
CONTROL_PORT |= CONTROL_MASK; // Enable internal pull-up resistors. Normal high operation. CONTROL_PORT |= CONTROL_MASK; // Enable internal pull-up resistors. Normal high operation.
#endif #endif
CONTROL_PCMSK |= CONTROL_MASK; // Enable specific pins of the Pin Change Interrupt CONTROL_PCMSK |= CONTROL_MASK; // Enable specific pins of the Pin Change Interrupt
PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt
} }
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where // Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is // triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is
// defined by the CONTROL_PIN_INDEX in the header file. // defined by the CONTROL_PIN_INDEX in the header file.
uint8_t system_control_get_state() uint8_t system_control_get_state() {
{ uint8_t control_state = 0;
uint8_t control_state = 0; uint8_t pin = (CONTROL_PIN & CONTROL_MASK) ^ CONTROL_MASK;
uint8_t pin = (CONTROL_PIN & CONTROL_MASK) ^ CONTROL_MASK; #ifdef INVERT_CONTROL_PIN_MASK
#ifdef INVERT_CONTROL_PIN_MASK
pin ^= INVERT_CONTROL_PIN_MASK; pin ^= INVERT_CONTROL_PIN_MASK;
#endif #endif
if (pin) { if (pin) {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(pin,(1<<CONTROL_SAFETY_DOOR_BIT))) { control_state |= CONTROL_PIN_INDEX_SAFETY_DOOR; } if (bit_istrue(pin, (1 << CONTROL_SAFETY_DOOR_BIT))) {
#else control_state |= CONTROL_PIN_INDEX_SAFETY_DOOR;
if (bit_istrue(pin,(1<<CONTROL_FEED_HOLD_BIT))) { control_state |= CONTROL_PIN_INDEX_FEED_HOLD; } }
#endif #else
if (bit_istrue(pin,(1<<CONTROL_RESET_BIT))) { control_state |= CONTROL_PIN_INDEX_RESET; } if (bit_istrue(pin, (1 << CONTROL_FEED_HOLD_BIT))) {
if (bit_istrue(pin,(1<<CONTROL_CYCLE_START_BIT))) { control_state |= CONTROL_PIN_INDEX_CYCLE_START; } control_state |= CONTROL_PIN_INDEX_FEED_HOLD;
} }
return(control_state); #endif
if (bit_istrue(pin, (1 << CONTROL_RESET_BIT))) {
control_state |= CONTROL_PIN_INDEX_RESET;
}
if (bit_istrue(pin, (1 << CONTROL_CYCLE_START_BIT))) {
control_state |= CONTROL_PIN_INDEX_CYCLE_START;
}
}
return (control_state);
} }
// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets // Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
// only the realtime command execute variable to have the main program execute these when // only the realtime command execute variable to have the main program execute these when
// its ready. This works exactly like the character-based realtime commands when picked off // its ready. This works exactly like the character-based realtime commands when picked off
// directly from the incoming serial data stream. // directly from the incoming serial data stream.
ISR(CONTROL_INT_vect) ISR(CONTROL_INT_vect) {
{ uint8_t pin = system_control_get_state();
uint8_t pin = system_control_get_state(); if (pin) {
if (pin) { if (bit_istrue(pin, CONTROL_PIN_INDEX_RESET)) {
if (bit_istrue(pin,CONTROL_PIN_INDEX_RESET)) { mc_reset();
mc_reset(); }
if (bit_istrue(pin, CONTROL_PIN_INDEX_CYCLE_START)) {
bit_true(sys_rt_exec_state, EXEC_CYCLE_START);
}
#ifndef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(pin, CONTROL_PIN_INDEX_FEED_HOLD)) {
bit_true(sys_rt_exec_state, EXEC_FEED_HOLD);
#else
if (bit_istrue(pin, CONTROL_PIN_INDEX_SAFETY_DOOR)) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
#endif
}
} }
if (bit_istrue(pin,CONTROL_PIN_INDEX_CYCLE_START)) {
bit_true(sys_rt_exec_state, EXEC_CYCLE_START);
}
#ifndef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(pin,CONTROL_PIN_INDEX_FEED_HOLD)) {
bit_true(sys_rt_exec_state, EXEC_FEED_HOLD);
#else
if (bit_istrue(pin,CONTROL_PIN_INDEX_SAFETY_DOOR)) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
#endif
}
}
} }
// Returns if safety door is ajar(T) or closed(F), based on pin state. // Returns if safety door is ajar(T) or closed(F), based on pin state.
uint8_t system_check_safety_door_ajar() uint8_t system_check_safety_door_ajar() {
{ #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN return (system_control_get_state() & CONTROL_PIN_INDEX_SAFETY_DOOR);
return(system_control_get_state() & CONTROL_PIN_INDEX_SAFETY_DOOR); #else
#else return (false); // Input pin not enabled, so just return that it's closed.
return(false); // Input pin not enabled, so just return that it's closed. #endif
#endif
} }
// Executes user startup script, if stored. // Executes user startup script, if stored.
void system_execute_startup(char *line) void system_execute_startup(char *line) {
{ uint8_t n;
uint8_t n; for (n = 0; n < N_STARTUP_LINE; n++) {
for (n=0; n < N_STARTUP_LINE; n++) { if (!(settings_read_startup_line(n, line))) {
if (!(settings_read_startup_line(n, line))) { line[0] = 0;
line[0] = 0; report_execute_startup_message(line, STATUS_SETTING_READ_FAIL);
report_execute_startup_message(line,STATUS_SETTING_READ_FAIL); } else {
} else { if (line[0] != 0) {
if (line[0] != 0) { uint8_t status_code = gc_execute_line(line);
uint8_t status_code = gc_execute_line(line); report_execute_startup_message(line, status_code);
report_execute_startup_message(line,status_code); }
} }
} }
}
} }
// Directs and executes one line of formatted input from protocol_process. While mostly // Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as // incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// settings, initiating the homing cycle, and toggling switch states. This differs from // settings, initiating the homing cycle, and toggling switch states. This differs from
@ -120,291 +117,331 @@ void system_execute_startup(char *line)
// the lines that are processed afterward, not necessarily real-time during a cycle, // the lines that are processed afterward, not necessarily real-time during a cycle,
// since there are motions already stored in the buffer. However, this 'lag' should not // since there are motions already stored in the buffer. However, this 'lag' should not
// be an issue, since these commands are not typically used during a cycle. // be an issue, since these commands are not typically used during a cycle.
uint8_t system_execute_line(char *line) uint8_t system_execute_line(char *line) {
{ uint8_t char_counter = 1;
uint8_t char_counter = 1; uint8_t helper_var = 0; // Helper variable
uint8_t helper_var = 0; // Helper variable float parameter, value;
float parameter, value; switch (line[char_counter]) {
switch( line[char_counter] ) { case 0: report_grbl_help(); break;
case 0 : report_grbl_help(); break; case 'J': // Jogging
case 'J' : // Jogging // Execute only if in IDLE or JOG states.
// Execute only if in IDLE or JOG states. if (sys.state != STATE_IDLE && sys.state != STATE_JOG) {
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) { return(STATUS_IDLE_ERROR); } return (STATUS_IDLE_ERROR);
if(line[2] != '=') { return(STATUS_INVALID_STATEMENT); } }
return(gc_execute_line(line)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions. if (line[2] != '=') {
break; return (STATUS_INVALID_STATEMENT);
case '$': case 'G': case 'C': case 'X': }
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); } return (gc_execute_line(line)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions.
switch( line[1] ) { break;
case '$' : // Prints Grbl settings case '$':
if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print. case 'G':
else { report_grbl_settings(); } case 'C':
break; case 'X':
case 'G' : // Prints gcode parser state if (line[2] != 0) {
// TODO: Move this to realtime commands for GUIs to request this data during suspend-state. return (STATUS_INVALID_STATEMENT);
report_gcode_modes(); }
break; switch (line[1]) {
case 'C' : // Set check g-code mode [IDLE/CHECK] case '$': // Prints Grbl settings
// Perform reset when toggling off. Check g-code mode should only work if Grbl if (sys.state & (STATE_CYCLE | STATE_HOLD)) {
// is idle and ready, regardless of alarm locks. This is mainly to keep things return (STATUS_IDLE_ERROR);
// simple and consistent. } // Block during cycle. Takes too long to print.
if ( sys.state == STATE_CHECK_MODE ) { else {
mc_reset(); report_grbl_settings();
report_feedback_message(MESSAGE_DISABLED); }
} else { break;
if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode. case 'G': // Prints gcode parser state
sys.state = STATE_CHECK_MODE; // TODO: Move this to realtime commands for GUIs to request this data during suspend-state.
report_feedback_message(MESSAGE_ENABLED); report_gcode_modes();
} break;
break; case 'C': // Set check g-code mode [IDLE/CHECK]
case 'X' : // Disable alarm lock [ALARM] // Perform reset when toggling off. Check g-code mode should only work if Grbl
if (sys.state == STATE_ALARM) { // is idle and ready, regardless of alarm locks. This is mainly to keep things
// Block if safety door is ajar. // simple and consistent.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); } if (sys.state == STATE_CHECK_MODE) {
report_feedback_message(MESSAGE_ALARM_UNLOCK); mc_reset();
sys.state = STATE_IDLE; report_feedback_message(MESSAGE_DISABLED);
// Don't run startup script. Prevents stored moves in startup from causing accidents. } else {
} // Otherwise, no effect. if (sys.state) {
break; return (STATUS_IDLE_ERROR);
} } // Requires no alarm mode.
break; sys.state = STATE_CHECK_MODE;
default : report_feedback_message(MESSAGE_ENABLED);
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing) }
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); } break;
switch( line[1] ) { case 'X': // Disable alarm lock [ALARM]
case '#' : // Print Grbl NGC parameters if (sys.state == STATE_ALARM) {
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); } // Block if safety door is ajar.
else { report_ngc_parameters(); } if (system_check_safety_door_ajar()) {
break; return (STATUS_CHECK_DOOR);
case 'H' : // Perform homing cycle [IDLE/ALARM] }
if (bit_isfalse(settings.flags,BITFLAG_HOMING_ENABLE)) {return(STATUS_SETTING_DISABLED); } report_feedback_message(MESSAGE_ALARM_UNLOCK);
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); } // Block if safety door is ajar. sys.state = STATE_IDLE;
sys.state = STATE_HOMING; // Set system state variable // Don't run startup script. Prevents stored moves in startup from causing accidents.
if (line[2] == 0) { } // Otherwise, no effect.
mc_homing_cycle(HOMING_CYCLE_ALL); break;
#ifdef HOMING_SINGLE_AXIS_COMMANDS }
break;
default:
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
if (!(sys.state == STATE_IDLE || sys.state == STATE_ALARM)) {
return (STATUS_IDLE_ERROR);
}
switch (line[1]) {
case '#': // Print Grbl NGC parameters
if (line[2] != 0) {
return (STATUS_INVALID_STATEMENT);
} else {
report_ngc_parameters();
}
break;
case 'H': // Perform homing cycle [IDLE/ALARM]
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) {
return (STATUS_SETTING_DISABLED);
}
if (system_check_safety_door_ajar()) {
return (STATUS_CHECK_DOOR);
} // Block if safety door is ajar.
sys.state = STATE_HOMING; // Set system state variable
if (line[2] == 0) {
mc_homing_cycle(HOMING_CYCLE_ALL);
#ifdef HOMING_SINGLE_AXIS_COMMANDS
} else if (line[3] == 0) { } else if (line[3] == 0) {
switch (line[2]) { switch (line[2]) {
case 'X': mc_homing_cycle(HOMING_CYCLE_X); break; case 'X': mc_homing_cycle(HOMING_CYCLE_X); break;
case 'Y': mc_homing_cycle(HOMING_CYCLE_Y); break; case 'Y': mc_homing_cycle(HOMING_CYCLE_Y); break;
case 'Z': mc_homing_cycle(HOMING_CYCLE_Z); break; case 'Z': mc_homing_cycle(HOMING_CYCLE_Z); break;
default: return(STATUS_INVALID_STATEMENT); default: return (STATUS_INVALID_STATEMENT);
} }
#endif #endif
} else { return(STATUS_INVALID_STATEMENT); } } else {
if (!sys.abort) { // Execute startup scripts after successful homing. return (STATUS_INVALID_STATEMENT);
sys.state = STATE_IDLE; // Set to IDLE when complete. }
st_go_idle(); // Set steppers to the settings idle state before returning. if (!sys.abort) { // Execute startup scripts after successful homing.
if (line[2] == 0) { system_execute_startup(line); } sys.state = STATE_IDLE; // Set to IDLE when complete.
} st_go_idle(); // Set steppers to the settings idle state before returning.
break; if (line[2] == 0) {
case 'S' : // Puts Grbl to sleep [IDLE/ALARM] system_execute_startup(line);
if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) { return(STATUS_INVALID_STATEMENT); } }
system_set_exec_state_flag(EXEC_SLEEP); // Set to execute sleep mode immediately
break;
case 'I' : // Print or store build info. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) {
settings_read_build_info(line);
report_build_info(line);
#ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
} else { // Store startup line [IDLE/ALARM]
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
settings_store_build_info(line);
#endif
}
break;
case 'R' : // Restore defaults [IDLE/ALARM]
if ((line[2] != 'S') || (line[3] != 'T') || (line[4] != '=') || (line[6] != 0)) { return(STATUS_INVALID_STATEMENT); }
switch (line[5]) {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS
case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_WIPE_ALL
case '*': settings_restore(SETTINGS_RESTORE_ALL); break;
#endif
default: return(STATUS_INVALID_STATEMENT);
}
report_feedback_message(MESSAGE_RESTORE_DEFAULTS);
mc_reset(); // Force reset to ensure settings are initialized correctly.
break;
case 'N' : // Startup lines. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) { // Print startup lines
for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
if (!(settings_read_startup_line(helper_var, line))) {
report_status_message(STATUS_SETTING_READ_FAIL);
} else {
report_startup_line(helper_var,line);
}
} }
break; break;
} else { // Store startup line [IDLE Only] Prevents motion during ALARM. case 'S': // Puts Grbl to sleep [IDLE/ALARM]
if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle. if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) {
helper_var = true; // Set helper_var to flag storing method. return (STATUS_INVALID_STATEMENT);
// No break. Continues into default: to read remaining command characters.
}
default : // Storing setting methods [IDLE/ALARM]
if(!read_float(line, &char_counter, &parameter)) { return(STATUS_BAD_NUMBER_FORMAT); }
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
if (helper_var) { // Store startup line
// Prepare sending gcode block to gcode parser by shifting all characters
helper_var = char_counter; // Set helper variable as counter to start of gcode block
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
// Execute gcode block to ensure block is valid.
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
if (helper_var) { return(helper_var); }
else {
helper_var = trunc(parameter); // Set helper_var to int value of parameter
settings_store_startup_line(helper_var,line);
} }
} else { // Store global setting. system_set_exec_state_flag(EXEC_SLEEP); // Set to execute sleep mode immediately
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } break;
if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); } case 'I': // Print or store build info. [IDLE/ALARM]
return(settings_store_global_setting((uint8_t)parameter, value)); if (line[++char_counter] == 0) {
} settings_read_build_info(line);
} report_build_info(line);
} #ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } else { // Store startup line [IDLE/ALARM]
if (line[char_counter++] != '=') {
return (STATUS_INVALID_STATEMENT);
}
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter - helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
settings_store_build_info(line);
#endif
}
break;
case 'R': // Restore defaults [IDLE/ALARM]
if ((line[2] != 'S') || (line[3] != 'T') || (line[4] != '=') || (line[6] != 0)) {
return (STATUS_INVALID_STATEMENT);
}
switch (line[5]) {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS
case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_WIPE_ALL
case '*': settings_restore(SETTINGS_RESTORE_ALL); break;
#endif
default: return (STATUS_INVALID_STATEMENT);
}
report_feedback_message(MESSAGE_RESTORE_DEFAULTS);
mc_reset(); // Force reset to ensure settings are initialized correctly.
break;
case 'N': // Startup lines. [IDLE/ALARM]
if (line[++char_counter] == 0) { // Print startup lines
for (helper_var = 0; helper_var < N_STARTUP_LINE; helper_var++) {
if (!(settings_read_startup_line(helper_var, line))) {
report_status_message(STATUS_SETTING_READ_FAIL);
} else {
report_startup_line(helper_var, line);
}
}
break;
} else { // Store startup line [IDLE Only] Prevents motion during ALARM.
if (sys.state != STATE_IDLE) {
return (STATUS_IDLE_ERROR);
} // Store only when idle.
helper_var = true; // Set helper_var to flag storing method.
// No break. Continues into default: to read remaining command characters.
}
default: // Storing setting methods [IDLE/ALARM]
if (!read_float(line, &char_counter, &parameter)) {
return (STATUS_BAD_NUMBER_FORMAT);
}
if (line[char_counter++] != '=') {
return (STATUS_INVALID_STATEMENT);
}
if (helper_var) { // Store startup line
// Prepare sending gcode block to gcode parser by shifting all characters
helper_var = char_counter; // Set helper variable as counter to start of gcode block
do {
line[char_counter - helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
// Execute gcode block to ensure block is valid.
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
if (helper_var) {
return (helper_var);
} else {
helper_var = trunc(parameter); // Set helper_var to int value of parameter
settings_store_startup_line(helper_var, line);
}
} else { // Store global setting.
if (!read_float(line, &char_counter, &value)) {
return (STATUS_BAD_NUMBER_FORMAT);
}
if ((line[char_counter] != 0) || (parameter > 255)) {
return (STATUS_INVALID_STATEMENT);
}
return (settings_store_global_setting((uint8_t)parameter, value));
}
}
}
return (STATUS_OK); // If '$' command makes it to here, then everything's ok.
} }
void system_flag_wco_change() {
#ifdef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE
void system_flag_wco_change()
{
#ifdef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE
protocol_buffer_synchronize(); protocol_buffer_synchronize();
#endif #endif
sys.report_wco_counter = 0; sys.report_wco_counter = 0;
} }
// Returns machine position of axis 'idx'. Must be sent a 'step' array. // Returns machine position of axis 'idx'. Must be sent a 'step' array.
// NOTE: If motor steps and machine position are not in the same coordinate frame, this function // NOTE: If motor steps and machine position are not in the same coordinate frame, this function
// serves as a central place to compute the transformation. // serves as a central place to compute the transformation.
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx) float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx) {
{ float pos;
float pos; #ifdef COREXY
#ifdef COREXY if (idx == X_AXIS) {
if (idx==X_AXIS) { pos = (float)system_convert_corexy_to_x_axis_steps(steps) / settings.steps_per_mm[idx];
pos = (float)system_convert_corexy_to_x_axis_steps(steps) / settings.steps_per_mm[idx]; } else if (idx == Y_AXIS) {
} else if (idx==Y_AXIS) { pos = (float)system_convert_corexy_to_y_axis_steps(steps) / settings.steps_per_mm[idx];
pos = (float)system_convert_corexy_to_y_axis_steps(steps) / settings.steps_per_mm[idx];
} else { } else {
pos = steps[idx]/settings.steps_per_mm[idx]; pos = steps[idx] / settings.steps_per_mm[idx];
} }
#else #else
pos = steps[idx]/settings.steps_per_mm[idx]; pos = steps[idx] / settings.steps_per_mm[idx];
#endif #endif
return(pos); return (pos);
} }
void system_convert_array_steps_to_mpos(float *position, int32_t *steps) {
void system_convert_array_steps_to_mpos(float *position, int32_t *steps) uint8_t idx;
{ for (idx = 0; idx < N_AXIS; idx++) {
uint8_t idx; position[idx] = system_convert_axis_steps_to_mpos(steps, idx);
for (idx=0; idx<N_AXIS; idx++) { }
position[idx] = system_convert_axis_steps_to_mpos(steps, idx); return;
}
return;
} }
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps. // CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
#ifdef COREXY #ifdef COREXY
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps) int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps) {
{ return ((steps[A_MOTOR] + steps[B_MOTOR]) / 2);
return( (steps[A_MOTOR] + steps[B_MOTOR])/2 );
}
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps)
{
return( (steps[A_MOTOR] - steps[B_MOTOR])/2 );
}
#endif
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target)
{
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#ifdef HOMING_FORCE_SET_ORIGIN
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
// NOTE: max_travel is stored as negative
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { return(true); }
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
#endif
}
return(false);
} }
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps) {
return ((steps[A_MOTOR] - steps[B_MOTOR]) / 2);
}
#endif
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target) {
uint8_t idx;
for (idx = 0; idx < N_AXIS; idx++) {
#ifdef HOMING_FORCE_SET_ORIGIN
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
// NOTE: max_travel is stored as negative
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) {
return (true);
}
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) {
return (true);
}
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) {
return (true);
}
#endif
}
return (false);
}
// Special handlers for setting and clearing Grbl's real-time execution flags. // Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask) { void system_set_exec_state_flag(uint8_t mask) {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_state |= (mask); sys_rt_exec_state |= (mask);
SREG = sreg; SREG = sreg;
} }
void system_clear_exec_state_flag(uint8_t mask) { void system_clear_exec_state_flag(uint8_t mask) {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_state &= ~(mask); sys_rt_exec_state &= ~(mask);
SREG = sreg; SREG = sreg;
} }
void system_set_exec_alarm(uint8_t code) { void system_set_exec_alarm(uint8_t code) {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_alarm = code; sys_rt_exec_alarm = code;
SREG = sreg; SREG = sreg;
} }
void system_clear_exec_alarm() { void system_clear_exec_alarm() {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_alarm = 0; sys_rt_exec_alarm = 0;
SREG = sreg; SREG = sreg;
} }
void system_set_exec_motion_override_flag(uint8_t mask) { void system_set_exec_motion_override_flag(uint8_t mask) {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_motion_override |= (mask); sys_rt_exec_motion_override |= (mask);
SREG = sreg; SREG = sreg;
} }
void system_set_exec_accessory_override_flag(uint8_t mask) { void system_set_exec_accessory_override_flag(uint8_t mask) {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_accessory_override |= (mask); sys_rt_exec_accessory_override |= (mask);
SREG = sreg; SREG = sreg;
} }
void system_clear_exec_motion_overrides() { void system_clear_exec_motion_overrides() {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_motion_override = 0; sys_rt_exec_motion_override = 0;
SREG = sreg; SREG = sreg;
} }
void system_clear_exec_accessory_overrides() { void system_clear_exec_accessory_overrides() {
uint8_t sreg = SREG; uint8_t sreg = SREG;
cli(); cli();
sys_rt_exec_accessory_override = 0; sys_rt_exec_accessory_override = 0;
SREG = sreg; SREG = sreg;
} }

View file

@ -28,141 +28,144 @@
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default // NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the realtime protocol only needs to check for a non-zero value to // flags are always false, so the realtime protocol only needs to check for a non-zero value to
// know when there is a realtime command to execute. // know when there is a realtime command to execute.
#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 #define EXEC_STATUS_REPORT bit(0) // bitmask 00000001
#define EXEC_CYCLE_START bit(1) // bitmask 00000010 #define EXEC_CYCLE_START bit(1) // bitmask 00000010
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 #define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000 #define EXEC_FEED_HOLD bit(3) // bitmask 00001000
#define EXEC_RESET bit(4) // bitmask 00010000 #define EXEC_RESET bit(4) // bitmask 00010000
#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000 #define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000 #define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
#define EXEC_SLEEP bit(7) // bitmask 10000000 #define EXEC_SLEEP bit(7) // bitmask 10000000
// Alarm executor codes. Valid values (1-255). Zero is reserved. // Alarm executor codes. Valid values (1-255). Zero is reserved.
#define EXEC_ALARM_HARD_LIMIT 1 #define EXEC_ALARM_HARD_LIMIT 1
#define EXEC_ALARM_SOFT_LIMIT 2 #define EXEC_ALARM_SOFT_LIMIT 2
#define EXEC_ALARM_ABORT_CYCLE 3 #define EXEC_ALARM_ABORT_CYCLE 3
#define EXEC_ALARM_PROBE_FAIL_INITIAL 4 #define EXEC_ALARM_PROBE_FAIL_INITIAL 4
#define EXEC_ALARM_PROBE_FAIL_CONTACT 5 #define EXEC_ALARM_PROBE_FAIL_CONTACT 5
#define EXEC_ALARM_HOMING_FAIL_RESET 6 #define EXEC_ALARM_HOMING_FAIL_RESET 6
#define EXEC_ALARM_HOMING_FAIL_DOOR 7 #define EXEC_ALARM_HOMING_FAIL_DOOR 7
#define EXEC_ALARM_HOMING_FAIL_PULLOFF 8 #define EXEC_ALARM_HOMING_FAIL_PULLOFF 8
#define EXEC_ALARM_HOMING_FAIL_APPROACH 9 #define EXEC_ALARM_HOMING_FAIL_APPROACH 9
#define EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH 10 #define EXEC_ALARM_HOMING_FAIL_DUAL_APPROACH 10
// Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides. // Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides.
// Spindle/coolant and feed/rapids are separated into two controlling flag variables. // Spindle/coolant and feed/rapids are separated into two controlling flag variables.
#define EXEC_FEED_OVR_RESET bit(0) #define EXEC_FEED_OVR_RESET bit(0)
#define EXEC_FEED_OVR_COARSE_PLUS bit(1) #define EXEC_FEED_OVR_COARSE_PLUS bit(1)
#define EXEC_FEED_OVR_COARSE_MINUS bit(2) #define EXEC_FEED_OVR_COARSE_MINUS bit(2)
#define EXEC_FEED_OVR_FINE_PLUS bit(3) #define EXEC_FEED_OVR_FINE_PLUS bit(3)
#define EXEC_FEED_OVR_FINE_MINUS bit(4) #define EXEC_FEED_OVR_FINE_MINUS bit(4)
#define EXEC_RAPID_OVR_RESET bit(5) #define EXEC_RAPID_OVR_RESET bit(5)
#define EXEC_RAPID_OVR_MEDIUM bit(6) #define EXEC_RAPID_OVR_MEDIUM bit(6)
#define EXEC_RAPID_OVR_LOW bit(7) #define EXEC_RAPID_OVR_LOW bit(7)
// #define EXEC_RAPID_OVR_EXTRA_LOW bit(*) // *NOT SUPPORTED* // #define EXEC_RAPID_OVR_EXTRA_LOW bit(*) // *NOT SUPPORTED*
#define EXEC_SPINDLE_OVR_RESET bit(0) #define EXEC_SPINDLE_OVR_RESET bit(0)
#define EXEC_SPINDLE_OVR_COARSE_PLUS bit(1) #define EXEC_SPINDLE_OVR_COARSE_PLUS bit(1)
#define EXEC_SPINDLE_OVR_COARSE_MINUS bit(2) #define EXEC_SPINDLE_OVR_COARSE_MINUS bit(2)
#define EXEC_SPINDLE_OVR_FINE_PLUS bit(3) #define EXEC_SPINDLE_OVR_FINE_PLUS bit(3)
#define EXEC_SPINDLE_OVR_FINE_MINUS bit(4) #define EXEC_SPINDLE_OVR_FINE_MINUS bit(4)
#define EXEC_SPINDLE_OVR_STOP bit(5) #define EXEC_SPINDLE_OVR_STOP bit(5)
#define EXEC_COOLANT_FLOOD_OVR_TOGGLE bit(6) #define EXEC_COOLANT_FLOOD_OVR_TOGGLE bit(6)
#define EXEC_COOLANT_MIST_OVR_TOGGLE bit(7) #define EXEC_COOLANT_MIST_OVR_TOGGLE bit(7)
// Define system state bit map. The state variable primarily tracks the individual functions // Define system state bit map. The state variable primarily tracks the individual functions
// of Grbl to manage each without overlapping. It is also used as a messaging flag for // of Grbl to manage each without overlapping. It is also used as a messaging flag for
// critical events. // critical events.
#define STATE_IDLE 0 // Must be zero. No flags. #define STATE_IDLE 0 // Must be zero. No flags.
#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access. #define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access.
#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only. #define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only.
#define STATE_HOMING bit(2) // Performing homing cycle #define STATE_HOMING bit(2) // Performing homing cycle
#define STATE_CYCLE bit(3) // Cycle is running or motions are being executed. #define STATE_CYCLE bit(3) // Cycle is running or motions are being executed.
#define STATE_HOLD bit(4) // Active feed hold #define STATE_HOLD bit(4) // Active feed hold
#define STATE_JOG bit(5) // Jogging mode. #define STATE_JOG bit(5) // Jogging mode.
#define STATE_SAFETY_DOOR bit(6) // Safety door is ajar. Feed holds and de-energizes system. #define STATE_SAFETY_DOOR bit(6) // Safety door is ajar. Feed holds and de-energizes system.
#define STATE_SLEEP bit(7) // Sleep state. #define STATE_SLEEP bit(7) // Sleep state.
// Define system suspend flags. Used in various ways to manage suspend states and procedures. // 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_HOLD_COMPLETE bit(0) // Indicates initial feed hold is complete. #define SUSPEND_HOLD_COMPLETE bit(0) // Indicates initial feed hold is complete.
#define SUSPEND_RESTART_RETRACT bit(1) // Flag to indicate a retract from a restore parking motion. #define SUSPEND_RESTART_RETRACT bit(1) // Flag to indicate a retract from a restore parking motion.
#define SUSPEND_RETRACT_COMPLETE bit(2) // (Safety door only) Indicates retraction and de-energizing is complete. #define SUSPEND_RETRACT_COMPLETE bit(2) // (Safety door only) Indicates retraction and de-energizing is complete.
#define SUSPEND_INITIATE_RESTORE bit(3) // (Safety door only) Flag to initiate resume procedures from a cycle start. #define SUSPEND_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_RESTORE_COMPLETE bit(4) // (Safety door only) Indicates ready to resume normal operation.
#define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Tracks safety door state for resuming. #define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Tracks safety door state for resuming.
#define SUSPEND_MOTION_CANCEL bit(6) // Indicates a canceled resume motion. Currently used by probing routine. #define SUSPEND_MOTION_CANCEL bit(6) // Indicates a canceled resume motion. Currently used by probing routine.
#define SUSPEND_JOG_CANCEL bit(7) // Indicates a jog cancel in process and to reset buffers when complete. #define SUSPEND_JOG_CANCEL bit(7) // Indicates a jog cancel in process and to reset buffers when complete.
// Define step segment generator state flags. // Define step segment generator state flags.
#define STEP_CONTROL_NORMAL_OP 0 // Must be zero. #define STEP_CONTROL_NORMAL_OP 0 // Must be zero.
#define STEP_CONTROL_END_MOTION bit(0) #define STEP_CONTROL_END_MOTION bit(0)
#define STEP_CONTROL_EXECUTE_HOLD bit(1) #define STEP_CONTROL_EXECUTE_HOLD bit(1)
#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2) #define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2)
#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3) #define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3)
// Define control pin index for Grbl internal use. Pin maps may change, but these values don't. // Define control pin index for Grbl internal use. Pin maps may change, but these values don't.
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
#define N_CONTROL_PIN 4 #define N_CONTROL_PIN 4
#define CONTROL_PIN_INDEX_SAFETY_DOOR bit(0) #define CONTROL_PIN_INDEX_SAFETY_DOOR bit(0)
#define CONTROL_PIN_INDEX_RESET bit(1) #define CONTROL_PIN_INDEX_RESET bit(1)
#define CONTROL_PIN_INDEX_FEED_HOLD bit(2) #define CONTROL_PIN_INDEX_FEED_HOLD bit(2)
#define CONTROL_PIN_INDEX_CYCLE_START bit(3) #define CONTROL_PIN_INDEX_CYCLE_START bit(3)
#else #else
#define N_CONTROL_PIN 3 #define N_CONTROL_PIN 3
#define CONTROL_PIN_INDEX_RESET bit(0) #define CONTROL_PIN_INDEX_RESET bit(0)
#define CONTROL_PIN_INDEX_FEED_HOLD bit(1) #define CONTROL_PIN_INDEX_FEED_HOLD bit(1)
#define CONTROL_PIN_INDEX_CYCLE_START bit(2) #define CONTROL_PIN_INDEX_CYCLE_START bit(2)
#endif #endif
// Define spindle stop override control states. // Define spindle stop override control states.
#define SPINDLE_STOP_OVR_DISABLED 0 // Must be zero. #define SPINDLE_STOP_OVR_DISABLED 0 // Must be zero.
#define SPINDLE_STOP_OVR_ENABLED bit(0) #define SPINDLE_STOP_OVR_ENABLED bit(0)
#define SPINDLE_STOP_OVR_INITIATE bit(1) #define SPINDLE_STOP_OVR_INITIATE bit(1)
#define SPINDLE_STOP_OVR_RESTORE bit(2) #define SPINDLE_STOP_OVR_RESTORE bit(2)
#define SPINDLE_STOP_OVR_RESTORE_CYCLE bit(3) #define SPINDLE_STOP_OVR_RESTORE_CYCLE bit(3)
// Define global system variables // Define global system variables
typedef struct { typedef struct {
uint8_t state; // Tracks the current system state of Grbl. uint8_t state; // Tracks the current system state of Grbl.
uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door. uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door.
uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean) uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean)
uint8_t step_control; // Governs the step segment generator depending on system state. uint8_t step_control; // Governs the step segment generator depending on system state.
uint8_t probe_succeeded; // Tracks if last probing cycle was successful. uint8_t probe_succeeded; // Tracks if last probing cycle was successful.
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
#ifdef ENABLE_DUAL_AXIS #ifdef ENABLE_DUAL_AXIS
uint8_t homing_axis_lock_dual; uint8_t homing_axis_lock_dual;
#endif #endif
uint8_t f_override; // Feed rate override value in percent uint8_t f_override; // Feed rate override value in percent
uint8_t r_override; // Rapids override value in percent uint8_t r_override; // Rapids override value in percent
uint8_t spindle_speed_ovr; // Spindle speed value in percent uint8_t spindle_speed_ovr; // Spindle speed value in percent
uint8_t spindle_stop_ovr; // Tracks spindle stop override states uint8_t spindle_stop_ovr; // Tracks spindle stop override states
uint8_t report_ovr_counter; // Tracks when to add override data to status reports. uint8_t report_ovr_counter; // Tracks when to add override data to status reports.
uint8_t report_wco_counter; // Tracks when to add work coordinate offset data to status reports. uint8_t report_wco_counter; // Tracks when to add work coordinate offset data to status reports.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
uint8_t override_ctrl; // Tracks override control states. uint8_t override_ctrl; // Tracks override control states.
#endif #endif
#ifdef VARIABLE_SPINDLE #ifdef VARIABLE_SPINDLE
float spindle_speed; float spindle_speed;
#endif #endif
} system_t; } system_t;
extern system_t sys; extern system_t sys;
// NOTE: These position variables may need to be declared as volatiles, if problems arise. // NOTE: These position variables may need to be declared as volatiles, if problems arise.
extern int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps. extern int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
extern int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps. extern int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
extern volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR. extern volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
extern volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks. extern volatile uint8_t
extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
extern volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides. extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
extern volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides. extern volatile uint8_t
sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
extern volatile uint8_t
sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG #ifdef DEBUG
#define EXEC_DEBUG_REPORT bit(0) #define EXEC_DEBUG_REPORT bit(0)
extern volatile uint8_t sys_rt_exec_debug; extern volatile uint8_t sys_rt_exec_debug;
#endif #endif
// Initialize the serial protocol // Initialize the serial protocol
@ -180,7 +183,6 @@ uint8_t system_execute_line(char *line);
// Execute the startup script lines stored in EEPROM upon initialization // Execute the startup script lines stored in EEPROM upon initialization
void system_execute_startup(char *line); void system_execute_startup(char *line);
void system_flag_wco_change(); void system_flag_wco_change();
// Returns machine position of axis 'idx'. Must be sent a 'step' array. // Returns machine position of axis 'idx'. Must be sent a 'step' array.
@ -191,8 +193,8 @@ void system_convert_array_steps_to_mpos(float *position, int32_t *steps);
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps. // CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
#ifdef COREXY #ifdef COREXY
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps); int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps);
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps); int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps);
#endif #endif
// Checks and reports if target array exceeds machine travel limits. // Checks and reports if target array exceeds machine travel limits.
@ -208,5 +210,4 @@ void system_set_exec_accessory_override_flag(uint8_t mask);
void system_clear_exec_motion_overrides(); void system_clear_exec_motion_overrides();
void system_clear_exec_accessory_overrides(); void system_clear_exec_accessory_overrides();
#endif #endif