void spindle_run(uint8_t direction, float rpm) { if (sys.state == STATE_CHECK_MODE) { return; } // Empty planner buffer to ensure spindle is set when programmed. protocol_auto_cycle_start(); //temp fix for M3 lockup protocol_buffer_synchronize(); if (direction == SPINDLE_DISABLE) { spindle_stop(); } else { #ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN if (direction == SPINDLE_ENABLE_CW) { SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT); } else { SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT); } #endif #ifdef VARIABLE_SPINDLE // TODO: Install the optional capability for frequency-based output for servos. #define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM) #define RC_SERVO_RANGE (RC_SERVO_LONG-RC_SERVO_SHORT) #ifdef CPU_MAP_ATMEGA2560 TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER); TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x07 | (1<<WAVE2_REGISTER) | (1<<WAVE3_REGISTER); // set to 1/1024 Prescaler OCR4A = 0xFFFF; // set the top 16bit value uint16_t current_pwm; #else TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER); TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x07; // set to 1/1024 Prescaler uint8_t current_pwm; #endif if ( rpm < SPINDLE_MIN_RPM ) { rpm = 0; } else { rpm -= SPINDLE_MIN_RPM; if ( rpm > SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent integer overflow } #ifdef RC_SERVO_INVERT current_pwm = floor( RC_SERVO_LONG - rpm*(RC_SERVO_RANGE/SPINDLE_RPM_RANGE)); OCR_REGISTER = current_pwm; #else current_pwm = floor( rpm*(RC_SERVO_RANGE/SPINDLE_RPM_RANGE) + RC_SERVO_SHORT); OCR_REGISTER = current_pwm; #endif #ifdef MINIMUM_SPINDLE_PWM if (current_pwm < MINIMUM_SPINDLE_PWM) { current_pwm = MINIMUM_SPINDLE_PWM; } OCR_REGISTER = current_pwm; #endif #endif } }
// Block until all buffered steps are executed or in a cycle state. Works with feed hold // during a synchronize call, if it should happen. Also, waits for clean cycle end. void protocol_buffer_synchronize() { // If system is queued, ensure cycle resumes if the auto start flag is present. protocol_auto_cycle_start(); 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)); }
void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) #endif #endif { // If enabled, check for soft limit violations. Placed here all line motions are picked up // from everywhere in Grbl. if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { limits_soft_check(target); } // If in check gcode mode, prevent motion by blocking planner. Soft limits still work. if (sys.state == STATE_CHECK_MODE) { return; } // NOTE: Backlash compensation may be installed here. It will need direction info to track when // to insert a backlash line motion(s) before the intended line motion and will require its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting // backlash steps will need to be also tracked, which will need to be kept at a system level. // There are likely some other things that will need to be tracked as well. However, we feel // that backlash compensation should NOT be handled by Grbl itself, because there are a myriad // of ways to implement it and can be effective or ineffective for different CNC machines. This // would be better handled by the interface as a post-processor task, where the original g-code // is translated and inserts backlash motions that best suits the machine. // NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that // indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but // doesn't update the machine position values. Since the position values used by the g-code // parser and planner are separate from the system machine positions, this is doable. // If the buffer is full: good! That means we are well ahead of the robot. // Remain in this loop until there is room in the buffer. do { protocol_execute_runtime(); // Check for any run-time commands if (sys.abort) { return; } // Bail, if system abort. if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full. else { break; } } while (1); #ifdef USE_LINE_NUMBERS plan_buffer_line(target, feed_rate, invert_feed_rate, line_number); #else #ifdef VARIABLE_SPINDLE // NNW plan_buffer_line(target, feed_rate, invert_feed_rate,rpm); #else plan_buffer_line(target, feed_rate, invert_feed_rate); #endif #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; } }
void spindle_run(uint8_t direction, float rpm) { if (sys.state == STATE_CHECK_MODE) { return; } // Empty planner buffer to ensure spindle is set when programmed. protocol_auto_cycle_start(); //temp fix for M3 lockup protocol_buffer_synchronize(); // Halt or set spindle direction and rpm. if (direction == SPINDLE_DISABLE) { spindle_stop(); } else { if (direction == SPINDLE_ENABLE_CW) { SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT); } else { SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT); } #ifdef VARIABLE_SPINDLE #define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM) TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER); TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler rpm -= SPINDLE_MIN_RPM; if ( rpm > SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; // Prevent uint8 overflow } uint8_t current_pwm = floor( rpm*(255.0/SPINDLE_RPM_RANGE) + 0.5); OCR_REGISTER = current_pwm; #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared. SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); #endif #else SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); #endif } }
/* GRBL PRIMARY LOOP: */ void protocol_main_loop() { // ------------------------------------------------------------ // Complete initialization procedures upon a power-up or reset. // ------------------------------------------------------------ // Print welcome message report_init_message(); // 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! But first check for safety door. #ifndef DEFAULTS_TRINAMIC 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. } #endif system_execute_startup(line); // Execute startup script. } // --------------------------------------------------------------------------------- // Primary loop! Upon a system abort, this exits back to main() to reset the system. // --------------------------------------------------------------------------------- uint8_t comment = COMMENT_NONE; uint8_t char_counter = 0; uint8_t c; for (;;) { // Process one line of incoming serial data, as the data becomes available. Performs an // initial filtering by removing spaces and comments and capitalizing all letters. // NOTE: While comment, spaces, and block delete(if supported) handling should technically // be done in the g-code parser, doing it here helps compress the incoming data into Grbl's // line buffer, which is limited in size. The g-code standard actually states a line can't // exceed 256 characters, but the Arduino Uno does not have the memory space for this. // With a better processor, it would be very easy to pull this initial parsing out as a // seperate task to be shared by the g-code parser and Grbl's system commands. while((c = serial_read()) != SERIAL_NO_DATA) { if ((c == '\n') || (c == '\r')) { // End of line reached line[char_counter] = 0; // Set string termination character. protocol_execute_line(line); // Line is complete. Execute it! comment = COMMENT_NONE; char_counter = 0; } else { if (comment != COMMENT_NONE) { // Throw away all comment characters if (c == ')') { // End of comment. Resume line. But, not if semicolon type comment. if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; } } } else { if (c <= ' ') { // Throw away whitepace and control characters } else if (c == '/') { // Block delete NOT SUPPORTED. Ignore character. // NOTE: If supported, would simply need to check the system if block delete is enabled. } else if (c == '(') { // Enable comments flag and ignore all characters until ')' or EOL. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. // In the future, we could simply remove the items within the comments, but retain the // comment control characters, so that the g-code parser can error-check it. comment = COMMENT_TYPE_PARENTHESES; } else if (c == ';') { // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. comment = COMMENT_TYPE_SEMICOLON; // TODO: Install '%' feature // } else if (c == '%') { // Program start-end percent sign NOT SUPPORTED. // NOTE: This maybe installed to tell Grbl when a program is running vs manual input, // where, during a program, the system auto-cycle start will continue to execute // everything until the next '%' sign. This will help fix resuming issues with certain // functions that empty the planner buffer to execute its task on-time. } else if (char_counter >= (LINE_BUFFER_SIZE-1)) { // Detect line buffer overflow. Report error and reset line buffer. report_status_message(STATUS_OVERFLOW); comment = COMMENT_NONE; char_counter = 0; } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { line[char_counter++] = c; } } } } // If there are no more characters in the serial read buffer to be processed and executed, // this indicates that g-code streaming has either filled the planner buffer or has // completed. In either case, auto-cycle start, if enabled, any queued moves. protocol_auto_cycle_start(); protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to main() program loop to reset system. } return; /* Never reached */ }