Overhauled state machine. New safety door feature.

- Overhauled the state machine and cleaned up its overall operation.
This involved creating a new ‘suspend’ state for what all external
commands, except real-time commands, are ignored. All hold type states
enter this suspend state.

- Removed ‘auto cycle start’ setting from Grbl. This was not used by
users in its intended way and is somewhat redundant, as GUI manage the
cycle start by streaming. It also muddled up how Grbl should interpret
how and when to execute a g-code block. Removing it made everything
much much simpler.

- Fixed a program pause bug when used with other buffer_sync commands.

- New safety door feature for OEMs. Immediately forces a feed hold and
then de-energizes the machine. Resuming is blocked until the door is
closed. When it is, it re-energizes the system and then resumes on the
normal toolpath.

- Safety door input pin is optional and uses the feed hold pin on A1.
Enabled by config.h define.

- Spindle and coolant re-energizing upon a safety door resume has a
programmable delay time to allow for complete spin up to rpm and
turning on the coolant before resuming motion.

- Safety door-style feed holds can be used instead of regular feed hold
(doesn’t de-energize the machine) with a ‘@‘ character. If the safety
door input pin is not enabled, the system can be resumed at any time.
This commit is contained in:
Sonny Jeon 2015-02-11 21:19:00 -07:00
parent 20c7750dab
commit 4bdc20ffb9
16 changed files with 237 additions and 92 deletions

View File

@ -54,6 +54,7 @@
#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
#define CMD_RESET 0x18 // ctrl-x.
#define CMD_SAFETY_DOOR '@' //0x13 // ctrl-s
// 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
@ -133,6 +134,18 @@
// NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless.
// #define ENABLE_M7 // Disabled by default. Uncomment to enable.
// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered,
// immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until
// the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the
// previous tool path, as if nothing happened.
// #define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable.
// After the safety door switch has been toggled and restored, this setting sets the power-up delay
// between restoring the spindle and coolant and resuming the cycle.
// NOTE: Delay value is defined in milliseconds from zero to 65,535.
#define SAFETY_DOOR_SPINDLE_DELAY 4000 // Disabled by default. Comment to enable.
#define SAFETY_DOOR_COOLANT_DELAY 1000 // Disabled by default. Comment to enable.
// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
// #define HOMING_CYCLE_0 (1<<X_AXIS) and #define HOMING_CYCLE_1 (1<<Y_AXIS)

View File

@ -108,10 +108,11 @@
#define RESET_BIT 0 // Uno Analog Pin 0
#define FEED_HOLD_BIT 1 // Uno Analog Pin 1
#define CYCLE_START_BIT 2 // Uno Analog Pin 2
#define SAFETY_DOOR_BIT 1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT1_vect
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT))
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
// Define probe switch input pin.
#define PROBE_DDR DDRC

View File

@ -1022,12 +1022,14 @@ uint8_t gc_execute_line(char *line)
gc_state.modal.program_flow = gc_block.modal.program_flow;
if (gc_state.modal.program_flow) {
protocol_buffer_synchronize(); // Finish all remaining buffered motions. Program paused when complete.
sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued.
sys.suspend = true;
protocol_execute_realtime(); // Suspend execution. For both program pause or program end.
// If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise,
// re-enable program flow after pause complete, where cycle start will resume the program.
if (gc_state.modal.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); }
else { gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; }
else { gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; } // Resume from program pause.
}
// TODO: % to denote start of program. Sets auto cycle start?

View File

@ -202,7 +202,11 @@ void limits_go_home(uint8_t cycle_mask)
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
// Check only for user reset. No time to run protocol_execute_realtime() in this loop.
if (sys.rt_exec_state & EXEC_RESET) { protocol_execute_realtime(); return; }
if (sys.rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET)) { // Abort homing and alarm upon safety door.
if (sys.rt_exec_state & EXEC_SAFETY_DOOR) { mc_reset(); }
protocol_execute_realtime();
return;
}
} while (STEP_MASK & axislock);
st_reset(); // Immediately force kill steppers and reset step segment buffer.
@ -285,7 +289,7 @@ void limits_go_home(uint8_t cycle_mask)
// Initiate pull-off using main motion control routines.
// TODO : Clean up state routines so that this motion still shows homing state.
sys.state = STATE_QUEUED;
sys.state = STATE_IDLE;
bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START);
protocol_execute_realtime();
protocol_buffer_synchronize(); // Complete pull-off motion.
@ -326,7 +330,7 @@ void limits_soft_check(float *target)
do {
protocol_execute_realtime();
if (sys.abort) { return; }
} while ( sys.state != STATE_IDLE || sys.state != STATE_QUEUED);
} while ( sys.state != STATE_IDLE );
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.

View File

@ -79,8 +79,7 @@ int main(void)
sys.abort = false;
sys.rt_exec_state = 0;
sys.rt_exec_alarm = 0;
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
else { sys.auto_start = false; }
sys.suspend = false;
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();

View File

@ -77,10 +77,6 @@
#else
plan_buffer_line(target, feed_rate, invert_feed_rate);
#endif
// If idle, indicate to the system there is now a planned block in the buffer ready to cycle
// start. Otherwise ignore and continue on.
if (!sys.state) { sys.state = STATE_QUEUED; }
}
@ -286,7 +282,6 @@ void mc_homing_cycle()
// Finish all queued commands and empty planner buffer before starting probe cycle.
protocol_buffer_synchronize();
uint8_t auto_start_state = sys.auto_start; // Store run state
// Initialize probing control variables
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
@ -315,7 +310,7 @@ void mc_homing_cycle()
do {
protocol_execute_realtime();
if (sys.abort) { return; } // Check for system abort
} while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED));
} while (sys.state != STATE_IDLE);
// Probing cycle complete!
@ -339,9 +334,6 @@ void mc_homing_cycle()
// NOTE: The target[] variable updated here will be sent back and synced with the g-code parser.
system_convert_array_steps_to_mpos(target, sys.position);
// Restore run state before returning
sys.auto_start = auto_start_state;
#ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message.
report_probe_parameters();
@ -364,11 +356,11 @@ void mc_reset()
spindle_stop();
coolant_stop();
// Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging
// 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
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off.
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) {
if ((sys.state & (STATE_CYCLE | STATE_HOMING)) || (sys.suspend == SUSPEND_ENABLE_HOLD)) {
bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE);
st_go_idle(); // Force kill steppers. Position has likely been lost.
}

View File

@ -62,7 +62,7 @@ void probe_state_monitor()
if (probe_get_state()) {
sys.probe_state = PROBE_OFF;
memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS);
bit_true(sys.rt_exec_state, EXEC_FEED_HOLD);
bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL);
}
}
}

View File

@ -73,8 +73,13 @@ void protocol_main_loop()
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_LOCK);
} else {
// All systems go!
// All systems go! But first check for safety door.
if (system_check_safety_door_ajar()) {
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
} else {
sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.
}
system_execute_startup(line); // Execute startup script.
}
@ -176,6 +181,8 @@ void protocol_execute_realtime()
{
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
do { // If system is suspended, suspend loop restarts here.
// Check and execute alarms.
rt_exec = sys.rt_exec_alarm; // Copy volatile sys.rt_exec_alarm.
if (rt_exec) { // Enter only if any bit flag is true
@ -210,6 +217,7 @@ void protocol_execute_realtime()
// Check amd execute realtime commands
rt_exec = sys.rt_exec_state; // Copy volatile sys.rt_exec_state.
if (rt_exec) { // Enter only if any bit flag is true
// Execute system abort.
if (rt_exec & EXEC_RESET) {
sys.abort = true; // Only place this is set true.
@ -222,29 +230,87 @@ void protocol_execute_realtime()
bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
}
// Execute a feed hold with deceleration, only during cycle.
if (rt_exec & EXEC_FEED_HOLD) {
// !!! During a cycle, the segment buffer has just been reloaded and full. So the math involved
// with the feed hold should be fine for most, if not all, operational scenarios.
// Execute hold states.
// NOTE: The math involved to calculate the hold should be low enough for most, if not all,
// operational scenarios. Once hold is initiated, the system enters a suspend state to block
// all main program processes until either reset or resumed.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {
// If in CYCLE state, all hold states immediately initiate a motion HOLD.
if (sys.state == STATE_CYCLE) {
sys.state = STATE_HOLD;
st_update_plan_block_parameters();
st_prep_buffer();
sys.auto_start = false; // Disable planner auto start upon feed hold.
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.suspend = SUSPEND_ENABLE_HOLD; // Initiate holding cycle with flag.
}
bit_false_atomic(sys.rt_exec_state,EXEC_FEED_HOLD);
// If IDLE, Grbl is not in motion. Simply indicate suspend ready state.
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_ENABLE_READY; }
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. If so, only flag that motion cancel is complete.
if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }
sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming. Special motion complete.
}
// Execute a cycle start by starting the stepper interrupt begin executing the blocks in queue.
// Execute a feed hold with deceleration, only during cycle.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD.
if (bit_isfalse(sys.state,STATE_SAFETY_DOOR)) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold, only during a cycle, and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged. The power-down is
// executed here, if IDLE, or when the CYCLE completes via the EXEC_CYCLE_STOP flag.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// If already in active, ready-to-resume HOLD, set CYCLE_STOP flag to force de-energize.
// NOTE: Only temporarily sets the 'rt_exec' variable, not the volatile 'rt_exec_state' variable.
if (sys.suspend & SUSPEND_ENABLE_READY) { bit_true(rt_exec,EXEC_CYCLE_STOP); }
sys.suspend |= SUSPEND_ENERGIZE;
sys.state = STATE_SAFETY_DOOR;
}
}
bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
if (rt_exec & EXEC_CYCLE_START) {
if (sys.state == STATE_QUEUED) {
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// Cycle start only when IDLE or when a hold is complete and ready to resume.
// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_ENABLE_READY))) {
// Re-energize powered components, if disabled by SAFETY_DOOR.
if (sys.suspend & SUSPEND_ENERGIZE) {
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed);
delay_ms(SAFETY_DOOR_SPINDLE_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
coolant_set_state(gc_state.modal.coolant);
delay_ms(SAFETY_DOOR_COOLANT_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
// TODO: Install return to pre-park position.
}
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
sys.state = STATE_CYCLE;
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
st_wake_up();
if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) {
sys.auto_start = true; // Re-enable auto start after feed hold.
} else {
sys.auto_start = false; // Reset auto start per settings.
} else { // Otherwise, do nothing. Set and resume IDLE state.
sys.state = STATE_IDLE;
}
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
}
}
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_START);
@ -256,17 +322,41 @@ void protocol_execute_realtime()
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if (rt_exec & EXEC_CYCLE_STOP) {
if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; }
else { sys.state = STATE_IDLE; }
if (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
if (sys.suspend & SUSPEND_ENERGIZE) { // De-energize system if safety door has been opened.
spindle_stop();
coolant_stop();
// TODO: Install parking motion here.
}
bit_true(sys.suspend,SUSPEND_ENABLE_READY);
} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states.
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP);
}
}
// Overrides flag byte (sys.override) and execution should be installed here, since they
// are realtime and require a direct and controlled interface to the main stepper program.
// Reload step segment buffer
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { st_prep_buffer(); }
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) { st_prep_buffer(); }
// If safety door was opened, actively check when safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (bit_istrue(sys.suspend,SUSPEND_ENABLE_READY)) {
if (!(system_check_safety_door_ajar())) {
sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume.
}
}
}
} while(sys.suspend); // Check for system suspend state before exiting.
}
@ -277,12 +367,10 @@ void protocol_buffer_synchronize()
{
// If system is queued, ensure cycle resumes if the auto start flag is present.
protocol_auto_cycle_start();
// Check and set auto start to resume cycle after synchronize and caller completes.
if (sys.state == STATE_CYCLE) { sys.auto_start = true; }
while (plan_get_current_block() || (sys.state == STATE_CYCLE)) {
do {
protocol_execute_realtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
}
} while (plan_get_current_block() || (sys.state == STATE_CYCLE));
}
@ -293,7 +381,8 @@ void protocol_buffer_synchronize()
// as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is
// manually issuing a cycle start command whenever the user is ready and there is a valid motion
// command in the planner queue.
// NOTE: This function is called from the main loop and mc_line() only and executes when one of
// two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished,
// single commands), or the planner buffer is full and ready to go.
void protocol_auto_cycle_start() { if (sys.auto_start) { bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); } }
// NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes
// when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming
// is finished, single commands), a command that needs to wait for the motions in the buffer to
// execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start() { bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); }

View File

@ -123,6 +123,8 @@ void report_feedback_message(uint8_t message_code)
printPgmString(PSTR("Enabled")); break;
case MESSAGE_DISABLED:
printPgmString(PSTR("Disabled")); break;
case MESSAGE_SAFETY_DOOR_AJAR:
printPgmString(PSTR("Check Door")); break;
}
printPgmString(PSTR("]\r\n"));
}
@ -171,8 +173,7 @@ void report_grbl_settings() {
printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance);
printPgmString(PSTR(" (arc tolerance, mm)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
printPgmString(PSTR(" (report inches, bool)\r\n$14=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
printPgmString(PSTR(" (auto start, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask);
@ -379,12 +380,13 @@ void report_realtime_status()
// Report current machine state
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
case STATE_QUEUED: printPgmString(PSTR("<Queue")); break;
case STATE_MOTION_CANCEL: // Report run state.
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_HOLD: printPgmString(PSTR("<Hold")); break;
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
case STATE_SAFETY_DOOR: printPgmString(PSTR("<Door")); break;
}
// If reporting a position, convert the current step count (current_position) to millimeters.

View File

@ -66,6 +66,7 @@
#define MESSAGE_ALARM_UNLOCK 3
#define MESSAGE_ENABLED 4
#define MESSAGE_DISABLED 5
#define MESSAGE_SAFETY_DOOR_AJAR 6
// Prints system status messages.
void report_status_message(uint8_t status_code);

View File

@ -172,6 +172,7 @@ ISR(SERIAL_RX)
case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true
case CMD_CYCLE_START: bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); break; // Set as true
case CMD_SAFETY_DOOR: bit_true_atomic(sys.rt_exec_state, EXEC_SAFETY_DOOR); break; // Set as true
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
default: // Write character to buffer
next_head = serial_rx_buffer_head + 1;

View File

@ -77,7 +77,6 @@ void settings_restore_global_settings() {
settings.flags = 0;
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; }
@ -248,10 +247,6 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; }
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
break;
case 14: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_AUTO_START; }
else { settings.flags &= ~BITFLAG_AUTO_START; }
break;
case 20:
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }

View File

@ -36,7 +36,7 @@
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
#define BITFLAG_AUTO_START bit(1)
// #define BITFLAG_AUTO_START bit(1) // Obsolete. Don't alter to keep back compatibility.
#define BITFLAG_INVERT_ST_ENABLE bit(2)
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
#define BITFLAG_HOMING_ENABLE bit(4)

View File

@ -529,6 +529,12 @@ void st_update_plan_block_parameters()
*/
void st_prep_buffer()
{
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Check if we still need to generate more segments for a motion suspend.
if (prep.current_speed == 0.0) { return; } // Nothing to do. Bail.
}
while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
// Determine if we need to load a new planner block or if the block has been replanned.
@ -571,7 +577,7 @@ void st_prep_buffer()
prep.dt_remainder = 0.0; // Reset for new planner block
if (sys.state == STATE_HOLD) {
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Override planner block entry speed and enforce deceleration during feed hold.
prep.current_speed = prep.exit_speed;
pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed;
@ -587,7 +593,7 @@ void st_prep_buffer()
*/
prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block.
float inv_2_accel = 0.5/pl_block->acceleration;
if (sys.state == STATE_HOLD) { // [Forced Deceleration to Zero Velocity]
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // [Forced Deceleration to Zero Velocity]
// Compute velocity profile parameters for a feed hold in-progress. This profile overrides
// the planner block profile, enforcing a deceleration to zero speed.
prep.ramp_type = RAMP_DECEL;
@ -746,16 +752,14 @@ void st_prep_buffer()
// Bail if we are at the end of a feed hold and don't have a step to execute.
if (prep_segment->n_step == 0) {
if (sys.state == STATE_HOLD) {
if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) {
// Less than one step to decelerate to zero speed, but already very close. AMASS
// requires full steps to execute. So, just bail.
prep.current_speed = 0.0;
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold.
prep.dt_remainder = 0.0;
prep.steps_remaining = n_steps_remaining;
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
plan_cycle_reinitialize();
sys.state = STATE_QUEUED;
return; // Segment not generated, but current step data still retained.
}
}
@ -818,21 +822,18 @@ void st_prep_buffer()
} else {
// End of planner block or forced-termination. No more distance to be executed.
if (mm_remaining > 0.0) { // At end of forced-termination.
// Reset prep parameters for resuming and then bail.
// NOTE: Currently only feed holds qualify for this scenario. May change with overrides.
prep.current_speed = 0.0;
// Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete
// the segment queue, where realtime protocol will set new state upon receiving the
// cycle stop flag from the ISR. Prep_segment is blocked until then.
prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold.
prep.dt_remainder = 0.0;
prep.steps_remaining = ceil(steps_remaining);
pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps.
plan_cycle_reinitialize();
sys.state = STATE_QUEUED; // End cycle.
return; // Bail!
// TODO: Try to move QUEUED setting into cycle re-initialize.
} else { // End of planner block
// The planner block is complete. All steps are set to be executed in the segment buffer.
pl_block = NULL;
pl_block = NULL; // Set pointer to indicate check and load next planner block.
plan_discard_current_block();
}
}
@ -848,7 +849,7 @@ void st_prep_buffer()
#ifdef REPORT_REALTIME_RATE
float st_get_realtime_rate()
{
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD)){
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR)){
return prep.current_speed;
}
return 0.0f;

View File

@ -48,15 +48,35 @@ ISR(CONTROL_INT_vect)
if (pin) {
if (bit_istrue(pin,bit(RESET_BIT))) {
mc_reset();
} else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) {
bit_true(sys.rt_exec_state, EXEC_FEED_HOLD);
} else if (bit_istrue(pin,bit(CYCLE_START_BIT))) {
bit_true(sys.rt_exec_state, EXEC_CYCLE_START);
#ifndef ENABLE_SAFETY_DOOR_INPUT_PIN
} else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) {
bit_true(sys.rt_exec_state, EXEC_FEED_HOLD);
#else
} else if (bit_istrue(pin,bit(SAFETY_DOOR_BIT))) {
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
#endif
}
}
}
// Returns if safety door is ajar(T) or closed(F), based on pin state.
uint8_t system_check_safety_door_ajar()
{
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
#ifdef INVERT_CONTROL_PIN
return(bit_istrue(CONTROL_PIN,bit(SAFETY_DOOR_BIT)));
#else
return(bit_isfalse(CONTROL_PIN,bit(SAFETY_DOOR_BIT)));
#endif
#else
return(false); // Input pin not enabled, so just return that it's closed.
#endif
}
// Executes user startup script, if stored.
void system_execute_startup(char *line)
{
@ -95,6 +115,7 @@ uint8_t system_execute_line(char *line)
else { report_grbl_settings(); }
break;
case 'G' : // Prints gcode parser state
// TODO: Move this to realtime commands for GUIs to request this data during suspend-state.
if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); }
else { report_gcode_modes(); }
break;
@ -118,6 +139,10 @@ uint8_t system_execute_line(char *line)
report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents.
if (system_check_safety_door_ajar()) { // Check safety door switch before returning.
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode.
}
} // Otherwise, no effect.
break;
// case 'J' : break; // Jogging methods
@ -144,6 +169,14 @@ uint8_t system_execute_line(char *line)
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
sys.state = STATE_HOMING; // Set system state variable
// Only perform homing if Grbl is idle or lost.
// TODO: Likely not required.
if (system_check_safety_door_ajar()) { // Check safety door switch before homing.
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode.
}
mc_homing_cycle();
if (!sys.abort) { // Execute startup scripts after successful homing.
sys.state = STATE_IDLE; // Set to IDLE when complete.

View File

@ -33,6 +33,8 @@
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000
#define EXEC_RESET bit(4) // bitmask 00010000
#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
// Alarm executor bit map.
// NOTE: EXEC_CRITICAL_EVENT is an optional flag that must be set with an alarm flag. When enabled,
@ -51,23 +53,30 @@
#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access.
#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only.
#define STATE_HOMING bit(2) // Performing homing cycle
#define STATE_QUEUED bit(3) // Indicates buffered blocks, awaiting cycle start.
#define STATE_CYCLE bit(4) // Cycle is running
#define STATE_HOLD bit(5) // Executing feed hold
// #define STATE_JOG bit(6) // Jogging mode is unique like homing.
#define STATE_CYCLE bit(3) // Cycle is running or motions are being executed.
#define STATE_HOLD bit(4) // Active feed hold
#define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system.
#define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle.
// Define system suspend states.
#define SUSPEND_DISABLE 0 // Must be zero.
#define SUSPEND_ENABLE_HOLD bit(0) // Enabled. Indicates the cycle is active and currently undergoing a hold.
#define SUSPEND_ENABLE_READY bit(1) // Ready to resume with a cycle start command.
#define SUSPEND_ENERGIZE bit(2) // Re-energizes output before resume.
#define SUSPEND_MOTION_CANCEL bit(3) // Cancels resume motion. Used by probing routine.
// Define global system variables
typedef struct {
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t state; // Tracks the current state of Grbl.
uint8_t suspend; // System suspend flag. Allows only realtime commands. Used primarily for holds.
volatile uint8_t rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
// NOTE: This may need to be a volatile variable, if problems arise.
uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
@ -80,6 +89,9 @@ extern system_t sys;
// Initialize the serial protocol
void system_init();
// Returns if safety door is open or closed, based on pin state.
uint8_t system_check_safety_door_ajar();
// Executes an internal system command, defined as a string starting with a '$'
uint8_t system_execute_line(char *line);