// 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 // (1 minute)/feed_rate time. // NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line // 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 make backlash compensation integration simple // and direct. // TODO: Check for a better way to avoid having to push the arguments twice for non-backlash cases. // However, this keeps the memory requirements lower since it doesn't have to call and hold two // plan_buffer_lines in memory. Grbl only has to retain the original line input variables during a // backlash segment(s). void mc_line(double x, double y, double z, double c, double feed_rate, uint8_t invert_feed_rate) { // TODO: Backlash compensation may be installed here. Only need direction info to track when // to insert a backlash line motion(s) before the intended line motion. Requires its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting // backlash steps will need to be also tracked. Not sure what the best strategy is for this, // i.e. keep the planner independent and do the computations in the status reporting, or let // the planner handle the position corrections. The latter may get complicated. // 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_runtime(); // Check for any run-time commands if (sys.abort) { return; } // Bail, if system abort. } while ( plan_check_full_buffer() ); plan_buffer_line(x, y, z, c, feed_rate, invert_feed_rate); // Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During // a feed hold, auto-start is disabled momentarily until the cycle is resumed by the cycle-start // runtime command. // NOTE: This is allows the user to decide to exclusively use the cycle start runtime command to // begin motion or let grbl auto-start it for them. This is useful when: manually cycle-starting // when the buffer is completely full and primed; auto-starting, if there was only one g-code // command sent during manual operation; or if a system is prone to buffer starvation, auto-start // helps make sure it minimizes any dwelling/motion hiccups and keeps the cycle going. if (sys.auto_start) { st_cycle_start(); } }
// 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_go_home() { sys.state = STATE_HOMING; // Set system state variable LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration limits_go_home(); // Perform homing routine. protocol_execute_runtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // The machine should now be homed and machine zero has been located. Upon completion, // reset system position and sync internal position vectors. clear_vector_float(sys.position); // Set machine zero sys_sync_current_position(); sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed. // Pull-off axes (that have been homed) from limit switches before continuing motion. // This provides some initial clearance off the switches and should also help prevent them // from falsely tripping when hard limits are enabled. /// 8c1 int8_t x_dir, y_dir, z_dir, t_dir; x_dir = y_dir = z_dir = t_dir = 0; if (HOMING_LOCATE_CYCLE & (1<<X_AXIS)) { if (settings.homing_dir_mask & (1<<X_DIRECTION_BIT)) x_dir = 1; else x_dir = -1; } if (HOMING_LOCATE_CYCLE & (1<<Y_AXIS)) { if (settings.homing_dir_mask & (1<<Y_DIRECTION_BIT)) { y_dir = 1; } else { y_dir = -1; } } if (HOMING_LOCATE_CYCLE & (1<<Z_AXIS)) { if (settings.homing_dir_mask & (1<<Z_DIRECTION_BIT)) { z_dir = 1; } else { z_dir = -1; } } /// 8c1 if (HOMING_LOCATE_CYCLE & (1<<T_AXIS)) { if (settings.homing_dir_mask & (1<<T_DIRECTION_BIT)) { t_dir = 1; } else { t_dir = -1; } } /// 8c1 : line mc_line(x_dir*settings.homing_pulloff, y_dir*settings.homing_pulloff, z_dir*settings.homing_pulloff, t_dir*settings.homing_pulloff, settings.homing_seek_rate, false, C_LINE); st_cycle_start(); // Move it. Nothing should be in the buffer except this motion. plan_synchronize(); // Make sure the motion completes. // The gcode parser position circumvented by the pull-off maneuver, so sync position vectors. sys_sync_current_position(); // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) LIMIT_PCMSK |= LIMIT_MASK; // Finished! }
// Returns the availability status of the block ring buffer. True, if full. uint8_t plan_check_full_buffer() { if (block_buffer_tail == next_buffer_head) { // TODO: Move this back into motion control. Shouldn't be here, but it's efficient. if (sys.auto_start) { st_cycle_start(); } // Auto-cycle start when buffer is full. return(true); } return(false); }
// Executes run-time commands, when required. This is called from various check points in the main // program, primarily where there may be a while loop waiting for a buffer to clear space or any // point where the execution time from the last check point may be more than a fraction of a second. // This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code // parsing and planning functions. This function also serves as an interface for the interrupts to // set the system runtime flags, where only the main program to handles them, removing the need to // define more computationally-expensive volatile variables. // NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted. void protocol_execute_runtime() { // evaluate emergency stop -cm if (!(AUX_PIN & (1<<AUX_STOP_BIT))) { if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. st_go_idle(); spindle_stop(); sys.execute |= EXEC_RESET; // Set as true } } if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { sys.abort = true; return; // Nothing else to do but exit. } // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { protocol_status_report(); bit_false(sys.execute,EXEC_STATUS_REPORT); } // Execute and serial print switches if (rt_exec & EXEC_SWITCH_REPORT) { protocol_switch_report(); bit_false(sys.execute,EXEC_SWITCH_REPORT); } // Initiate stepper feed hold if (rt_exec & EXEC_FEED_HOLD) { st_feed_hold(); // Initiate feed hold. bit_false(sys.execute,EXEC_FEED_HOLD); } // Reinitializes the stepper module running flags and re-plans the buffer after a feed hold. // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. if (rt_exec & EXEC_CYCLE_STOP) { st_cycle_reinitialize(); bit_false(sys.execute,EXEC_CYCLE_STOP); } if (rt_exec & EXEC_CYCLE_START) { st_cycle_start(); // Issue cycle start command to stepper subsystem #ifdef CYCLE_AUTO_START sys.auto_start = true; // Re-enable auto start after feed hold. #endif bit_false(sys.execute,EXEC_CYCLE_START); } } }
// 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_go_home() { sys.state = STATE_HOMING; // Set system state variable LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration limits_go_home(); // Perform homing routine. protocol_execute_runtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // The machine 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 machine zero appropriately. // At the same time, 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 tripping when hard limits are enabled. // TODO: Need to improve dir_mask[] to be more axes independent. float pulloff_target[N_AXIS]; clear_vector_float(pulloff_target); // Zero pulloff target. clear_vector_long(sys.position); // Zero current position for now. uint8_t dir_mask[N_AXIS]; dir_mask[X_AXIS] = (1<<X_DIRECTION_BIT); dir_mask[Y_AXIS] = (1<<Y_DIRECTION_BIT); dir_mask[Z_AXIS] = (1<<Z_DIRECTION_BIT); uint8_t i; for (i=0; i<N_AXIS; i++) { // 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. if (HOMING_LOCATE_CYCLE & bit(i)) { if (settings.homing_dir_mask & dir_mask[i]) { pulloff_target[i] = settings.homing_pulloff-settings.max_travel[i]; sys.position[i] = -lround(settings.max_travel[i]*settings.steps_per_mm[i]); } else { pulloff_target[i] = -settings.homing_pulloff; } } } sys_sync_current_position(); sys.state = STATE_IDLE; // Set system state to IDLE to complete motion and indicate homed. mc_line(pulloff_target, settings.homing_seek_rate, false); st_cycle_start(); // Move it. Nothing should be in the buffer except this motion. plan_synchronize(); // Make sure the motion completes. // The gcode parser position circumvented by the pull-off maneuver, so sync position vectors. sys_sync_current_position(); // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; } // Finished! }
// 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 // (1 minute)/feed_rate time. // NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line // 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 make backlash compensation integration simple // and direct. // TODO: Check for a better way to avoid having to push the arguments twice for non-backlash cases. // However, this keeps the memory requirements lower since it doesn't have to call and hold two // plan_buffer_lines in memory. Grbl only has to retain the original line input variables during a // backlash segment(s). void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate) { // TODO: Perform soft limit check here. Just check if the target x,y,z values are outside the // work envelope. Should be straightforward and efficient. By placing it here, rather than in // the g-code parser, it directly picks up motions from everywhere in Grbl. // If in check gcode mode, prevent motion by blocking planner. if (sys.state == STATE_CHECK_MODE) { return; } // TODO: Backlash compensation may be installed here. Only need direction info to track when // to insert a backlash line motion(s) before the intended line motion. Requires its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting // backlash steps will need to be also tracked. Not sure what the best strategy is for this, // i.e. keep the planner independent and do the computations in the status reporting, or let // the planner handle the position corrections. The latter may get complicated. // 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_runtime(); // Check for any run-time commands if (sys.abort) { return; // Bail, if system abort. } } while ( plan_check_full_buffer() ); plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); // 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; } // Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During // a feed hold, auto-start is disabled momentarily until the cycle is resumed by the cycle-start // runtime command. // NOTE: This is allows the user to decide to exclusively use the cycle start runtime command to // begin motion or let grbl auto-start it for them. This is useful when: manually cycle-starting // when the buffer is completely full and primed; auto-starting, if there was only one g-code // command sent during manual operation; or if a system is prone to buffer starvation, auto-start // helps make sure it minimizes any dwelling/motion hiccups and keeps the cycle going. if (sys.auto_start) { st_cycle_start(); } }
// Executes run-time commands, when required. This is called from various check points in the main // program, primarily where there may be a while loop waiting for a buffer to clear space or any // point where the execution time from the last check point may be more than a fraction of a second. // This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code // parsing and planning functions. This function also serves as an interface for the interrupts to // set the system runtime flags, where only the main program handles them, removing the need to // define more computationally-expensive volatile variables. This also provides a controlled way to // execute certain tasks without having two or more instances of the same task, such as the planner // recalculating the buffer upon a feedhold or override. // NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, // limit switches, or the main program. void protocol_execute_runtime() { if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times // System alarm. Everything has shutdown by something that has gone severely wrong. Report // the source of the error to the user. If critical, Grbl disables by entering an infinite // loop until system reset/abort. if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) { sys.state = STATE_ALARM; // Set system alarm state // Critical event. Only hard limit qualifies. Update this as new critical events surface. if (rt_exec & EXEC_CRIT_EVENT) { report_alarm_message(ALARM_HARD_LIMIT); report_feedback_message(MESSAGE_CRITICAL_EVENT); bit_false(sys.execute,EXEC_RESET); // Disable any existing reset do { // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits // typically occur while unattended or not paying attention. Gives the user time // to do what is needed before resetting, like killing the incoming stream. } while (bit_isfalse(sys.execute,EXEC_RESET)); // Standard alarm event. Only abort during motion qualifies. } else { // Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the // user that position may have been lost and set alarm state to enable the alarm lockout // to indicate the possible severity of the problem. report_alarm_message(ALARM_ABORT_CYCLE); } bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); } // Execute system abort. if (rt_exec & EXEC_RESET) { sys.abort = true; // Only place this is set true. return; // Nothing else to do but exit. } // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { report_realtime_status(); bit_false(sys.execute,EXEC_STATUS_REPORT); } // Initiate stepper feed hold if (rt_exec & EXEC_FEED_HOLD) { st_feed_hold(); // Initiate feed hold. bit_false(sys.execute,EXEC_FEED_HOLD); } // Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer. // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. if (rt_exec & EXEC_CYCLE_STOP) { st_cycle_reinitialize(); bit_false(sys.execute,EXEC_CYCLE_STOP); } if (rt_exec & EXEC_CYCLE_START) { st_cycle_start(); // Issue cycle start command to stepper subsystem if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; // Re-enable auto start after feed hold. } bit_false(sys.execute,EXEC_CYCLE_START); } } // Overrides flag byte (sys.override) and execution should be installed here, since they // are runtime and require a direct and controlled interface to the main stepper program. }
// Auto-cycle start is a user setting that automatically begins the cycle when a user enters // a valid motion command either manually or by a streaming tool. This is intended as a beginners // feature to help new users to understand g-code. It can be disabled. Otherwise, the normal // 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 mc_auto_cycle_start() { if (sys.auto_start) { st_cycle_start(); } }
int main(void) { #ifdef PART_LM4F120H5QR // ARM code SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN ); //set system clock to 80 MHz FPUEnable(); //enable the Floating Point Unit // FPULazyStackingEnable(); // Enable stacking for interrupt handlers #endif // Initialize system serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load grbl settings from EEPROM st_init(); // Setup stepper pins and interrupt timers #ifdef PART_LM4F120H5QR // ARM code IntMasterEnable(); #else // AVR code sei(); // Enable interrupts #endif memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position for(;;) { // Execute system reset upon a system abort, where the main program will return to this loop. // Once here, it is safe to re-initialize the system. At startup, the system will automatically // reset to finish the initialization process. if (sys.abort) { // Reset system. serial_reset_read_buffer(); // Clear serial read buffer plan_init(); // Clear block buffer and planner variables gc_init(); // Set g-code parser to default state protocol_init(); // Clear incoming line data and execute startup lines spindle_init(); coolant_init(); limits_init(); st_reset(); // Clear stepper subsystem variables. // Sync cleared gcode and planner positions to current system position, which is only // cleared upon startup, not a reset/abort. sys_sync_current_position(); // Reset system variables. sys.abort = false; sys.execute = 0; if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } // 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 (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } #endif // Check for and report alarm state after a reset, error, or an initial power up. if (sys.state == STATE_ALARM) { report_feedback_message(MESSAGE_ALARM_LOCK); } else { // All systems go. Set system to ready and execute startup script. sys.state = STATE_IDLE; protocol_execute_startup(); } } protocol_execute_runtime(); protocol_process(); // ... process the serial protocol // When the serial protocol returns, there are no more characters in the serial read buffer to // be processed and executed. This indicates that individual commands are being issued or // streaming is finished. In either case, auto-cycle start, if enabled, any queued moves. if (sys.auto_start) { st_cycle_start(); } } // return 0; /* never reached */ }
void protocol_execute_runtime() { uint8_t aux_bits; aux_bits = AUX_PIN ^ AUX_INVMASK; // apply invert mask if (!(aux_bits & (1<<AUX_STOP_BIT))) { if (!(emerg_stop)) { st_go_idle(); spindle_stop(); sys.abort = true; emerg_stop = true; } } else { emerg_stop = false; } AUX_PORT ^= (1<<AUX_WDOG_BIT); // Chargepump-Bit invertieren if (sys.execute) { // Enter only if any bit flag is true uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times // System abort. Steppers have already been force stopped. if (rt_exec & EXEC_RESET) { sys.abort = true; return; // Nothing else to do but exit. } // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { protocol_status_report(); bit_false(sys.execute,EXEC_STATUS_REPORT); } // Execute and serial print switches if (rt_exec & EXEC_SWITCH_REPORT) { protocol_switch_report(); bit_false(sys.execute,EXEC_SWITCH_REPORT); } // Initiate stepper feed hold if (rt_exec & EXEC_FEED_HOLD) { st_feed_hold(); // Initiate feed hold. bit_false(sys.execute,EXEC_FEED_HOLD); } // Reinitializes the stepper module running flags and re-plans the buffer after a feed hold. // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. if (rt_exec & EXEC_CYCLE_STOP) { st_cycle_reinitialize(); bit_false(sys.execute,EXEC_CYCLE_STOP); } if (rt_exec & EXEC_CYCLE_START) { st_cycle_start(); // Issue cycle start command to stepper subsystem #ifdef CYCLE_AUTO_START sys.auto_start = true; // Re-enable auto start after feed hold. #endif bit_false(sys.execute,EXEC_CYCLE_START); } } }