// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing // limit switch can cause a lot of problems, like false readings and multiple interrupt calls. // If a switch is triggered at all, something bad has happened and treat it as such, regardless // if a limit switch is being disengaged. It's impossible to reliably tell the state of a // bouncing pin without a debouncing method. A simple software debouncing feature may be enabled // through the config.h file, where an extra timer delays the limit pin read by several milli- // seconds to help with, not fix, bouncing switches. // NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during // homing cycles and will not respond correctly. Upon user request or need, there may be a // special pinout for an e-stop, but it is generally recommended to just directly connect // your e-stop switch to the Arduino reset pin, since it is the most correct way to do this. //#ifndef ENABLE_SOFTWARE_DEBOUNCE // ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process. void limitpin_check(void) { static uint8_t oldlimit; uint8_t newlimit; if(!limitintison) return; newlimit = limits_get_state(); if(newlimit == oldlimit) return; oldlimit = newlimit; // Ignore limit switches if already in an alarm state or in-process of executing an alarm. // When in the alarm state, Grbl should have been reset or will force a reset, so any pending // moves in the planner and serial buffers are all cleared and newly sent blocks will be // locked out until a homing cycle or a kill lock command. Allows the user to disable the hard // limit setting if their limits are constantly triggering after a reset and move their axes. if (sys.state != STATE_ALARM) { if (!(sys_rt_exec_alarm)) { #ifdef HARD_LIMIT_FORCE_STATE_CHECK // Check limit pin state. if ( limits_get_state() == 0) return; #endif mc_reset(); // Initiate system kill. if(newlimit & 0x8) system_set_exec_alarm_flag((EXEC_ALARM_STEPPER_FAIL|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event else system_set_exec_alarm_flag((EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate hard limit critical event } } }
// 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(); }
// 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(uint8_t cycle_mask) { // 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. system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); return; } #endif limits_disable(); // Disable hard limits pin change register for cycle duration // ------------------------------------------------------------------------------------- // Perform homing routine. NOTE: Special motion case. Only system reset works. #ifdef HOMING_SINGLE_AXIS_COMMANDS if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask. else #endif { // Search to engage all axes limit switches at faster homing seek rate. limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 #ifdef HOMING_CYCLE_1 limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 #endif #ifdef HOMING_CYCLE_2 limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 #endif } protocol_execute_realtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // Homing cycle complete! Setup system for normal operation. // ------------------------------------------------------------------------------------- // Sync gcode parser and planner positions to homed position. gc_sync_position(); plan_sync_position(); // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. limits_init(); }
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after // completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate // the trigger point of the limit switches. The rapid stops are handled by a system level axis lock // mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically // circumvent the processes for executing motions in normal operation. // NOTE: Only the abort realtime command can interrupt this process. // TODO: Move limit pin-specific calls to a general function for portability. void limits_go_home(uint8_t cycle_mask) { if (sys.abort) { return; } // Block if system reset has been issued. // Initialize uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1); uint8_t step_pin[N_AXIS]; float target[N_AXIS]; float max_travel = 0.0; uint8_t idx; for (idx=0; idx<N_AXIS; idx++) { // Initialize step pin masks step_pin[idx] = get_step_pin_mask(idx); #ifdef COREXY if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); } #endif 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]); } } // 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; 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++; sys.position[idx] = 0; // 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]; } } homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate. sys.homing_axis_lock = axislock; plan_sync_position(); // Sync planner position to current machine position. // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle. #ifdef USE_LINE_NUMBERS plan_buffer_line(target, homing_rate, false, false, HOMING_CYCLE_LINE_NUMBER); // Bypass mc_line(). Directly plan homing motion. #else plan_buffer_line(target, homing_rate, false, 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 { 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)) { axislock &= ~(step_pin[idx]); } } } sys.homing_axis_lock = axislock; } 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)) { // Homing failure: Limit switches are still engaged after pull-off motion if ( (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET)) || // Safety door or reset issued (!approach && (limits_get_state() & cycle_mask)) || // Limit switch still engaged after pull-off motion ( approach && (sys_rt_exec_state & EXEC_CYCLE_STOP)) ) { // Limit switch not found during approach. 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; } } } while (STEP_MASK & axislock); st_reset(); // Immediately force kill steppers and reset step segment buffer. plan_reset(); // Reset planner buffer to zero planner current position and to clear previous motions. 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. #ifdef COREXY int32_t off_axis_position = 0; #endif int32_t set_axis_position; // Set machine positions for homed limit switches. Don't update non-homed axes. for (idx=0; idx<N_AXIS; idx++) { // NOTE: settings.max_travel[] is stored as a negative value. if (cycle_mask & bit(idx)) { #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) { off_axis_position = (sys.position[B_MOTOR] - sys.position[A_MOTOR])/2; sys.position[A_MOTOR] = set_axis_position - off_axis_position; sys.position[B_MOTOR] = set_axis_position + off_axis_position; } else if (idx==Y_AXIS) { off_axis_position = (sys.position[A_MOTOR] + sys.position[B_MOTOR])/2; sys.position[A_MOTOR] = off_axis_position - set_axis_position; sys.position[B_MOTOR] = off_axis_position + set_axis_position; } else { sys.position[idx] = set_axis_position; } #else sys.position[idx] = set_axis_position; #endif } } plan_sync_position(); // Sync planner position to homed machine position. // sys.state = STATE_HOMING; // Ensure system state set as homing before returning. }
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram // and the actual location of the CNC machine. Users may change the following function to their // specific needs, but the desired real-time data report must be as short as possible. This is // requires as it minimizes the computational overhead and allows grbl to keep running smoothly, // especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz). void report_realtime_status() { // **Under construction** Bare-bones status report. Provides real-time machine position relative to // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). Eventually // to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask // for a user to select the desired real-time data. uint8_t idx; int32_t current_position[N_AXIS]; // Copy current state of the system position variable memcpy(current_position,sys.position,sizeof(sys.position)); float print_position[N_AXIS]; // Report current machine state switch (sys.state) { case STATE_IDLE: printPgmString(PSTR("<Idle")); break; case STATE_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. if (bit_istrue(settings.status_report_mask,(BITFLAG_RT_STATUS_MACHINE_POSITION | BITFLAG_RT_STATUS_WORK_POSITION))) { system_convert_array_steps_to_mpos(print_position,current_position); } // Report machine position if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_MACHINE_POSITION)) { printPgmString(PSTR(",MPos:")); for (idx=0; idx< N_AXIS; idx++) { printFloat_CoordValue(print_position[idx]); if (idx < (N_AXIS-1)) { printPgmString(PSTR(",")); } } } // Report work position if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_WORK_POSITION)) { printPgmString(PSTR(",WPos:")); for (idx=0; idx< N_AXIS; idx++) { // Apply work coordinate offsets and tool length offset to current position. print_position[idx] -= gc_state.coord_system[idx]+gc_state.coord_offset[idx]; if (idx == TOOL_LENGTH_OFFSET_AXIS) { print_position[idx] -= gc_state.tool_length_offset; } printFloat_CoordValue(print_position[idx]); if (idx < (N_AXIS-1)) { printPgmString(PSTR(",")); } } } // Returns the number of active blocks are in the planner buffer. if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_PLANNER_BUFFER)) { printPgmString(PSTR(",Buf:")); print_uint8_base10(plan_get_block_buffer_count()); } // Report serial read buffer status if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_SERIAL_RX)) { printPgmString(PSTR(",RX:")); print_uint8_base10(serial_get_rx_buffer_count()); } #ifdef USE_LINE_NUMBERS // Report current line number printPgmString(PSTR(",Ln:")); int32_t ln=0; plan_block_t * pb = plan_get_current_block(); if(pb != NULL) { ln = pb->line_number; } printInteger(ln); #endif #ifdef REPORT_REALTIME_RATE // Report realtime rate printPgmString(PSTR(",F:")); printFloat_RateValue(st_get_realtime_rate()); #endif if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) { printPgmString(PSTR(",Lim:")); print_unsigned_int8(limits_get_state(),2,N_AXIS); } #ifdef REPORT_CONTROL_PIN_STATE printPgmString(PSTR(",Ctl:")); print_uint8_base2(CONTROL_PIN & CONTROL_MASK); #endif printPgmString(PSTR(">\r\n")); }