int ISR(SERIAL_RX) { uint8_t data = UDR0; uint8_t next_head; // Pick off runtime command characters directly from the serial stream. These characters are // not passed into the buffer, but these set system state flag bits for runtime execution. switch (data) { case CMD_STATUS_REPORT: bit_true_atomic(sys.execute, EXEC_STATUS_REPORT); break; // Set as true case CMD_CYCLE_START: bit_true_atomic(sys.execute, EXEC_CYCLE_START); break; // Set as true case CMD_FEED_HOLD: bit_true_atomic(sys.execute, EXEC_FEED_HOLD); break; // Set as true case CMD_RESET: mc_reset(); break; // Call motion control reset routine. default: // Write character to buffer next_head = serial_rx_buffer_head + 1; if (next_head == RX_BUFFER_SIZE) { next_head = 0; } // Write data to buffer unless it is full. if (next_head != serial_rx_buffer_tail) { serial_rx_buffer[serial_rx_buffer_head] = data; serial_rx_buffer_head = next_head; #ifdef ENABLE_XONXOFF if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { flow_ctrl = SEND_XOFF; UCSR0B |= (1 << UDRIE0); // Force TX } #endif } //TODO: else alarm on overflow? } }
void serial_rxint(void) { uint8_t data = usart_recv(USART2);; uint32_t next_head; // Pick off realtime command characters directly from the serial stream. These characters are // not passed into the buffer, but these set system state flag bits for realtime execution. switch (data) { case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true case CMD_CYCLE_START: bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true case CMD_FEED_HOLD: bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); break; // Set as true case CMD_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; if (next_head == RX_BUFFER_SIZE) { next_head = 0; } // Write data to buffer unless it is full. if (next_head != serial_rx_buffer_tail) { serial_rx_buffer[serial_rx_buffer_head] = data; serial_rx_buffer_head = next_head; #ifdef ENABLE_XONXOFF if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { flow_ctrl = SEND_XOFF; usart_enable_tx_interrupt(USART2); } #endif } //TODO: else alarm on overflow? } }
// Method to ready the system to reset by setting the runtime reset command and killing any // active processes in the system. This also checks if a system reset is issued while Grbl // is in a motion state. If so, kills the steppers and sets the system alarm to flag position // lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by // runtime abort command and hard limits. So, keep to a minimum. void mc_reset() { // Only this function can set the system reset. Helps prevent multiple kill calls. if (bit_isfalse(sys.execute, EXEC_RESET)) { bit_true_atomic(sys.execute, EXEC_RESET); /*// Kill spindle and coolant. spindle_stop(); coolant_stop();*/ // Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging // NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps // the steppers enabled by avoiding the go_idle call altogether, unless the motion state is // violated, by which, all bets are off. if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { bit_true_atomic(sys.execute, EXEC_ALARM); // Flag main program to execute alarm state. st_go_idle(); // Force kill steppers. Position has likely been lost. } } }
// Method to ready the system to reset by setting the realtime reset command and killing any // active processes in the system. This also checks if a system reset is issued while Grbl // is in a motion state. If so, kills the steppers and sets the system alarm to flag position // lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by // realtime abort command and hard limits. So, keep to a minimum. void mc_reset() { // Only this function can set the system reset. Helps prevent multiple kill calls. if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) { bit_true_atomic(sys_rt_exec_state, EXEC_RESET); // Kill spindle and coolant. spindle_stop(); coolant_stop(); // Kill steppers only if in any motion state, i.e. cycle, 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_HOMING)) || (sys.suspend == SUSPEND_ENABLE_HOLD)) { if (sys.state == STATE_HOMING) { bit_true_atomic(sys_rt_exec_alarm, EXEC_ALARM_HOMING_FAIL); } else { bit_true_atomic(sys_rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE); } st_go_idle(); // Force kill steppers. Position has likely been lost. } } }
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed, // the workspace volume is in all negative space, and the system is in normal operation. void limits_soft_check(float *target) { uint8_t idx; uint8_t soft_limit_error = false; for (idx=0; idx<N_AXIS; idx++) { #ifdef HOMING_FORCE_SET_ORIGIN // When homing forced set origin is enabled, soft limits checks need to account for directionality. // NOTE: max_travel is stored as negative if (bit_istrue(settings.homing_dir_mask,bit(idx))) { if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { soft_limit_error = true; } } else { if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; } } #else // NOTE: max_travel is stored as negative if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; } #endif if (soft_limit_error) { // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within // workspace volume so just come to a controlled stop so position is not lost. When complete // enter alarm mode. if (sys.state == STATE_CYCLE) { bit_true_atomic(sys.execute, EXEC_FEED_HOLD); do { protocol_execute_runtime(); if (sys.abort) { return; } } while ( sys.state != STATE_IDLE || sys.state != STATE_QUEUED); } mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. bit_true_atomic(sys.execute, (EXEC_ALARM | EXEC_CRIT_EVENT)); // Indicate soft limit critical event protocol_execute_runtime(); // Execute to enter critical event loop and system abort return; } } }
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command. // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before // executing the homing cycle. This prevents incorrect buffered plans after homing. void mc_homing_cycle() { // Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems // with machines with limits wired on both ends of travel to one limit pin. // TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function. #ifdef LIMITS_TWO_SWITCHES_ON_AXES if (limits_get_state()) { mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. bit_true_atomic(sys_rt_exec_alarm, (EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); return; } #endif limits_disable(); // Disable hard limits pin change register for cycle duration // ------------------------------------------------------------------------------------- // Perform homing routine. NOTE: Special motion case. Only system reset works. // Search to engage all axes limit switches at faster homing seek rate. limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 #ifdef HOMING_CYCLE_1 limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 #endif #ifdef HOMING_CYCLE_2 limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 #endif #ifdef HOMING_CYCLE_3 limits_go_home(HOMING_CYCLE_3); // Homing cycle 3 #endif #ifdef HOMING_CYCLE_4 limits_go_home(HOMING_CYCLE_4); // Homing cycle 4 #endif #ifdef HOMING_CYCLE_5 limits_go_home(HOMING_CYCLE_5); // Homing cycle 5 #endif protocol_execute_realtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // Homing cycle complete! Setup system for normal operation. // ------------------------------------------------------------------------------------- // Gcode parser position was circumvented by the limits_go_home() routine, so sync position now. gc_sync_position(); // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. limits_init(); }
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away, uint8_t is_no_error) #endif { // TODO: Need to update this cycle so it obeys a non-auto cycle start. if (sys.state == STATE_CHECK_MODE) { return; } // Finish all queued commands and empty planner buffer before starting probe cycle. protocol_buffer_synchronize(); // Initialize probing control variables sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle. probe_configure_invert_mask(is_probe_away); // After syncing, check if probe is already triggered. If so, halt and issue alarm. // NOTE: This probe initialization error applies to all probing cycles. if ( probe_get_state() ) { // Check probe pin state. bit_true_atomic(sys_rt_exec_alarm, EXEC_ALARM_PROBE_FAIL); protocol_execute_realtime(); } if (sys.abort) { return; } // Return if system reset has been issued. // Setup and queue probing motion. Auto cycle-start should not start the cycle. #ifdef USE_LINE_NUMBERS mc_line(target, feed_rate, invert_feed_rate, line_number); #else mc_line(target, feed_rate, invert_feed_rate); #endif // Activate the probing state monitor in the stepper module. sys_probe_state = PROBE_ACTIVE; // Perform probing cycle. Wait here until probe is triggered or motion completes. bit_true_atomic(sys_rt_exec_state, EXEC_CYCLE_START); do { protocol_execute_realtime(); if (sys.abort) { return; } // Check for system abort } while (sys.state != STATE_IDLE); // Probing cycle complete! // Set state variables and error out, if the probe failed and cycle with error is enabled. if (sys_probe_state == PROBE_ACTIVE) { if (is_no_error) { memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); } else { bit_true_atomic(sys_rt_exec_alarm, EXEC_ALARM_PROBE_FAIL); } } else { sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully. } sys_probe_state = PROBE_OFF; // Ensure probe state monitor is disabled. protocol_execute_realtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort // Reset the stepper and planner buffers to remove the remainder of the probe motion. st_reset(); // Reest step segment buffer. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. plan_sync_position(); // Sync planner position to current machine position. // TODO: Update the g-code parser code to not require this target calculation but uses a gc_sync_position() call. // NOTE: The target[] variable updated here will be sent back and synced with the g-code parser. system_convert_array_steps_to_mpos(target, sys.position); #ifdef MESSAGE_PROBE_COORDINATES // All done! Output the probe position as message. report_probe_parameters(); #endif }
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after // completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate // the trigger point of the limit switches. The rapid stops are handled by a system level axis lock // mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically // circumvent the processes for executing motions in normal operation. // NOTE: Only the abort runtime command can interrupt this process. void limits_go_home(uint8_t cycle_mask) { if (sys.abort) { return; } // Block if system reset has been issued. // Initialize homing in search mode to quickly engage the specified cycle_mask limit switches. bool approach = true; float homing_rate = settings.homing_seek_rate; uint8_t invert_pin, idx; uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1); ///***5 float target[N_AXIS]; uint8_t limit_pin[N_AXIS], step_pin[N_AXIS]; float max_travel = 0.0; for (idx=0; idx<N_AXIS; idx++) { // Initialize limit and step pin masks limit_pin[idx] = get_limit_pin_mask(idx); ///////****get the Pin of limit min x, y ,z , Limit OK step_pin[idx] = get_step_pin_mask(idx); ///////****get the Pin of limit min x, y ,z now I let it 0, 1, 2 // Determine travel distance to the furthest homing switch based on user max travel settings. // NOTE: settings.max_travel[] is stored as a negative value. if (max_travel > settings.max_travel[idx]) { max_travel = settings.max_travel[idx]; } } max_travel *= -HOMING_AXIS_SEARCH_SCALAR; // Ensure homing switches engaged by over-estimating max travel. plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions. do { // Initialize invert_pin boolean based on approach and invert pin user setting. if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { invert_pin = approach; } else { invert_pin = !approach; } // Initialize and declare variables needed for homing routine. uint8_t n_active_axis = 0; uint8_t axislock = 0; for (idx=0; idx<N_AXIS; idx++) { // Set target location for active axes and setup computation for homing rate. if (bit_istrue(cycle_mask,bit(idx))) { n_active_axis++; if (!approach) { target[idx] = -max_travel; } else { target[idx] = max_travel; } } else { target[idx] = 0.0; } // Set target direction based on cycle mask if (bit_istrue(settings.homing_dir_mask,bit(idx))) { target[idx] = -target[idx]; } // Apply axislock to the step port pins active in this cycle. if (bit_istrue(cycle_mask,bit(idx))) { axislock |= step_pin[idx]; } } homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate. sys.homing_axis_lock = axislock; // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. uint8_t limit_state; #ifdef USE_LINE_NUMBERS plan_buffer_line(target, homing_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion. #else plan_buffer_line(target, homing_rate, false); // Bypass mc_line(). Directly plan homing motion. #endif st_prep_buffer(); // Prep and fill segment buffer from newly planned block. st_wake_up(); // Initiate motion do { // Check limit state. Lock out cycle axes when they change. /////////////////########################################*********** #if MotherBoard==3 /////**MB==3 #ifdef limit_int_style limit_state = LIMIT_PIN; if (invert_pin) { limit_state ^= LIMIT_MASK; } #else //************Get Limit Pin State. PiBot get limit/endstop mask same with the step mask. #if LIMIT_MAX_OPEN if(isXMinEndstopHit()) { limit_state^= MASK(X_LIMIT_BIT);} ////***read X_endstop then write into limite_state if(isYMinEndstopHit()) { limit_state^= MASK(Y_LIMIT_BIT);} if(isZMinEndstopHit()) { limit_state^= MASK(Z_LIMIT_BIT);} ////*****add MAX limit if(isXMaxEndstopHit()) { limit_state^= MASK(X_LIMIT_MAX_BIT);} ////***read X_endstop then write into limite_state if(isYMaxEndstopHit()) { limit_state^= MASK(Y_LIMIT_MAX_BIT);} if(isZMaxEndstopHit()) { limit_state^= MASK(Z_LIMIT_MAX_BIT);} ////*****add MAX limit #else if(isXMinEndstopHit()) { limit_state^= MASK(X_MASK);} ////***read X_endstop then write into limite_state if(isYMinEndstopHit()) { limit_state^= MASK(Y_MASK);} if(isZMinEndstopHit()) { limit_state^= MASK(Z_MASK);} ////*****add MAX limit #endif #endif #else /////**MB!=3 ////////################################################# limit_state = LIMIT_PIN; if (invert_pin) { limit_state ^= LIMIT_MASK; } #endif /////**MB==3 /////////////////////////////##################################### for (idx=0; idx<N_AXIS; idx++) { if (axislock & step_pin[idx]) { if (limit_state & limit_pin[idx]) { axislock &= ~(step_pin[idx]); } } } sys.homing_axis_lock = axislock; st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us. // Check only for user reset. No time to run protocol_execute_runtime() in this loop. if (sys.execute & EXEC_RESET) { protocol_execute_runtime(); return; } } while (STEP_MASK & axislock); st_reset(); // Immediately force kill steppers and reset step segment buffer. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure homing motion is cleared. delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate. // Reverse direction and reset homing rate for locate cycle(s). homing_rate = settings.homing_feed_rate; approach = !approach; } while (n_cycle-- > 0); // The active cycle axes should now be homed and machine limits have been located. By // default, grbl defines machine space as all negative, as do most CNCs. Since limit switches // can be on either side of an axes, check and set axes machine zero appropriately. Also, // set up pull-off maneuver from axes limit switches that have been homed. This provides // some initial clearance off the switches and should also help prevent them from falsely // triggering when hard limits are enabled or when more than one axes shares a limit pin. for (idx=0; idx<N_AXIS; idx++) { // Set up pull off targets and machine positions for limit switches homed in the negative // direction, rather than the traditional positive. Leave non-homed positions as zero and // do not move them. // NOTE: settings.max_travel[] is stored as a negative value. if (cycle_mask & bit(idx)) { #ifdef HOMING_FORCE_SET_ORIGIN sys.position[idx] = 0; // Set axis homed location as axis origin target[idx] = settings.homing_pulloff; if ( bit_isfalse(settings.homing_dir_mask,bit(idx)) ) { target[idx] = -target[idx]; } #else if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) { target[idx] = settings.homing_pulloff+settings.max_travel[idx]; sys.position[idx] = lround(settings.max_travel[idx]*settings.steps_per_mm[idx]); } else { target[idx] = -settings.homing_pulloff; sys.position[idx] = 0; } #endif } else { // Non-active cycle axis. Set target to not move during pull-off. target[idx] = (float)sys.position[idx]/settings.steps_per_mm[idx]; } } plan_sync_position(); // Sync planner position to current machine position for pull-off move. #ifdef USE_LINE_NUMBERS plan_buffer_line(target, settings.homing_seek_rate, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan motion. #else plan_buffer_line(target, settings.homing_seek_rate, false); // Bypass mc_line(). Directly plan motion. #endif // Initiate pull-off using main motion control routines. // TODO : Clean up state routines so that this motion still shows homing state. sys.state = STATE_QUEUED; bit_true_atomic(sys.execute, EXEC_CYCLE_START); protocol_execute_runtime(); protocol_buffer_synchronize(); // Complete pull-off motion. // Set system state to homing before returning. sys.state = STATE_HOMING; }
void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate) #endif { // TODO: Need to update this cycle so it obeys a non-auto cycle start. if (sys.state == STATE_CHECK_MODE) { return; } // Finish all queued commands and empty planner buffer before starting probe cycle. protocol_buffer_synchronize(); uint8_t auto_start_state = sys.auto_start; // Store run state // After syncing, check if probe is already triggered. If so, halt and issue alarm. if (probe_get_state()) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); protocol_execute_runtime(); } if (sys.abort) { return; } // Return if system reset has been issued. // Setup and queue probing motion. Auto cycle-start should not start the cycle. #ifdef USE_LINE_NUMBERS mc_line(target, feed_rate, invert_feed_rate, line_number); #else mc_line(target, feed_rate, invert_feed_rate); #endif // Activate the probing monitor in the stepper module. sys.probe_state = PROBE_ACTIVE; // Perform probing cycle. Wait here until probe is triggered or motion completes. bit_true_atomic(sys.execute, EXEC_CYCLE_START); do { protocol_execute_runtime(); if (sys.abort) { return; } // Check for system abort } while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED)); // Probing motion complete. If the probe has not been triggered, error out. if (sys.probe_state == PROBE_ACTIVE) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); } protocol_execute_runtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort // Reset the stepper and planner buffers to remove the remainder of the probe motion. st_reset(); // Reest step segment buffer. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. plan_sync_position(); // Sync planner position to current machine position. // Pull-off triggered probe to the trigger location since we had to decelerate a little beyond // it to stop the machine in a controlled manner. uint8_t idx; for(idx=0; idx<N_AXIS; idx++){ // NOTE: The target[] variable updated here will be sent back and synced with the g-code parser. target[idx] = (float)sys.probe_position[idx]/settings.steps_per_deg[idx]; } #ifdef USE_LINE_NUMBERS mc_line(target, feed_rate, invert_feed_rate, line_number); #else mc_line(target, feed_rate, invert_feed_rate); #endif // Execute pull-off motion and wait until it completes. bit_true_atomic(sys.execute, EXEC_CYCLE_START); protocol_buffer_synchronize(); if (sys.abort) { return; } // Return if system reset has been issued. sys.auto_start = auto_start_state; // Restore run state before returning #ifdef MESSAGE_PROBE_COORDINATES // All done! Output the probe position as message. report_probe_parameters(); #endif }
// Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that // requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that // automatically begins the cycle when a user enters a valid motion command manually. This is // intended as a beginners feature to help new users to understand g-code. It can be disabled // as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is // manually issuing a cycle start command whenever the user is ready and there is a valid motion // command in the planner queue. // NOTE: This function is called from the main loop, 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); }
// Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that // requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that // automatically begins the cycle when a user enters a valid motion command manually. This is // intended as a beginners feature to help new users to understand g-code. It can be disabled // as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is // manually issuing a cycle start command whenever the user is ready and there is a valid motion // command in the planner queue. // NOTE: This function is called from the main loop and mc_line() only and executes when one of // two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished, // single commands), or the planner buffer is full and ready to go. void Grbl::Protocol::auto_cycle_start() { if (parent->m_system.sys.auto_start) { bit_true_atomic(parent->m_system.sys.execute, EXEC_CYCLE_START); } }