diff --git a/Makefile b/Makefile index 8ff9359..ea1ea61 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ CLOCK = 16000000 PROGRAMMER ?= -c avrisp2 -P usb OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \ protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \ - print.o report.o + print.o report.o system.o # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m # update that line with this when programmer is back up: diff --git a/config.h b/config.h index 78d0ecf..dbf37e3 100644 --- a/config.h +++ b/config.h @@ -79,6 +79,11 @@ // parser state depending on user preferences. #define N_STARTUP_LINE 2 // Integer (1-3) +// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno +// analog pin 5. Only use this option if you require a second control pin. +// NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless. +// #define ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable. + // --------------------------------------------------------------------------------------- // ADVANCED CONFIGURATION OPTIONS: @@ -103,7 +108,14 @@ // NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch! // The hardware PWM output on pin D11 is required for variable spindle output voltages. // #define VARIABLE_SPINDLE // Default disabled. Uncomment to enable. -// #define SPINDLE_MAX_RPM 1000 // Max spindle RPM. This value is equal to 100% Duty Cycle on the PWM. + +// Use by the variable spindle output only. These parameters set the maximum and minimum spindle speed +// "S" g-code values to correspond to the maximum and minimum pin voltages. There are 256 discrete and +// equally divided voltage bins between the maximum and minimum spindle speeds. So for a 5V pin, 1000 +// max rpm, and 250 min rpm, the spindle output voltage would be set for the following "S" commands: +// "S1000" @ 5V, "S250" @ 0.02V, and "S625" @ 2.5V (mid-range). The pin outputs 0V when disabled. +#define SPINDLE_MAX_RPM 1000.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM. +#define SPINDLE_MIN_RPM 0.0 // Min spindle RPM. This value is equal to (1/256) duty cycle on the PWM. // Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at // every buffer block junction, except for starting from rest and end of the buffer, which are always @@ -182,12 +194,13 @@ // case, please report any successes to grbl administrators! // #define ENABLE_XONXOFF // Default disabled. Uncomment to enable. -// A simple software debouncing feature for hard limit switches. When enabled, the interrupt monitoring -// the hard limit switch pins will enable the Arduino's watchdog timer to re-check the limit pin state -// after a delay of about 32msec. This can help with CNC machines with problematic false triggering of -// their hard limit switches, but it WILL NOT fix issues with electrical interference on the signal -// cables from external sources. It's recommended to first use shielded signal cables that are grounded -// (old USB/computer cables work well) and wire in a low-pass circuit into each limit pin. +// A simple software debouncing feature for hard limit switches. When enabled, the interrupt +// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check +// the limit pin state after a delay of about 32msec. This can help with CNC machines with +// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with +// electrical interference on the signal cables from external sources. It's recommended to first +// use shielded signal cables with their shielding connected to ground (old USB/computer cables +// work well and are cheap to find) and wire in a low-pass circuit into each limit pin. // #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable. // --------------------------------------------------------------------------------------- diff --git a/coolant_control.c b/coolant_control.c index b849a78..593fc7c 100644 --- a/coolant_control.c +++ b/coolant_control.c @@ -18,19 +18,13 @@ along with Grbl. If not, see . */ +#include "system.h" #include "coolant_control.h" -#include "settings.h" -#include "config.h" #include "planner.h" -#include - -static uint8_t current_coolant_mode; - void coolant_init() { - current_coolant_mode = COOLANT_DISABLE; COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); #ifdef ENABLE_M7 COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT); @@ -50,20 +44,16 @@ void coolant_stop() void coolant_run(uint8_t mode) { - if (mode != current_coolant_mode) - { - plan_synchronize(); // Ensure coolant turns on when specified in program. - if (mode == COOLANT_FLOOD_ENABLE) { - COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); - + plan_synchronize(); // Ensure coolant turns on when specified in program. + if (mode == COOLANT_FLOOD_ENABLE) { + COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT); + #ifdef ENABLE_M7 } else if (mode == COOLANT_MIST_ENABLE) { COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT); #endif - } else { - coolant_stop(); - } - current_coolant_mode = mode; + } else { + coolant_stop(); } } diff --git a/coolant_control.h b/coolant_control.h index 38f3b43..c19a673 100644 --- a/coolant_control.h +++ b/coolant_control.h @@ -21,12 +21,12 @@ #ifndef coolant_control_h #define coolant_control_h -#include #define COOLANT_MIST_ENABLE 2 #define COOLANT_FLOOD_ENABLE 1 #define COOLANT_DISABLE 0 // Must be zero. + void coolant_init(); void coolant_stop(); void coolant_run(uint8_t mode); diff --git a/cpu_map.h b/cpu_map.h index bbc006b..39a3bb4 100644 --- a/cpu_map.h +++ b/cpu_map.h @@ -36,9 +36,6 @@ #define SERIAL_RX USART_RX_vect #define SERIAL_UDRE USART_UDRE_vect - // Start of PWM & Stepper Enabled Spindle - // #define VARIABLE_SPINDLE // comment this out to disable PWM & Stepper on the spindle - // Define step pulse output pins. NOTE: All step bit pins must be on the same port. #define STEPPING_DDR DDRD #define STEPPING_PORT PORTD diff --git a/defaults.h b/defaults.h index cbee821..a1de3fe 100644 --- a/defaults.h +++ b/defaults.h @@ -47,7 +47,7 @@ #define DEFAULT_DIRECTION_INVERT_MASK ((1< -#include "nuts_bolts.h" -#include +#include "system.h" #include "settings.h" +#include "gcode.h" +#include "planner.h" #include "motion_control.h" #include "spindle_control.h" #include "coolant_control.h" -#include "errno.h" -#include "protocol.h" #include "report.h" // Declare gc extern struct @@ -186,9 +183,9 @@ uint8_t gc_execute_line(char *line) case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause case 1: break; // Optional stop not supported. Ignore. case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset - case 3: gc.spindle_direction = 1; break; - case 4: gc.spindle_direction = -1; break; - case 5: gc.spindle_direction = 0; break; + case 3: gc.spindle_direction = SPINDLE_ENABLE_CW; break; + case 4: gc.spindle_direction = SPINDLE_ENABLE_CCW; break; + case 5: gc.spindle_direction = SPINDLE_DISABLE; break; #ifdef ENABLE_M7 case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break; #endif @@ -238,7 +235,7 @@ uint8_t gc_execute_line(char *line) case 'R': gc.arc_radius = to_millimeters(value); break; case 'S': if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative - gc.spindle_speed = value; + gc.spindle_speed = value; break; case 'T': if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative diff --git a/gcode.h b/gcode.h index aebc0ba..78d6128 100644 --- a/gcode.h +++ b/gcode.h @@ -21,8 +21,8 @@ #ifndef gcode_h #define gcode_h -#include -#include "nuts_bolts.h" + +#include "system.h" // Define modal group internal numbers for checking multiple command violations and tracking the // type of command that is called in the block. A modal group is a group of g-code commands that are @@ -70,12 +70,12 @@ typedef struct { uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} uint8_t program_flow; // {M0, M1, M2, M30} - int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} - uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9} + uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable, 2 = Mist Enable {M8, M9} + int8_t spindle_direction; // 1 = CW, 2 = CCW, 0 = Stop {M3, M4, M5} + float spindle_speed; // RPM float feed_rate; // Millimeters/min float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; - uint16_t spindle_speed; // RPM uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // The axes of the selected plane diff --git a/limits.c b/limits.c index 0abbc7d..169ca71 100644 --- a/limits.c +++ b/limits.c @@ -19,18 +19,12 @@ along with Grbl. If not, see . */ -#include -#include -#include -#include -#include "stepper.h" +#include "system.h" #include "settings.h" -#include "nuts_bolts.h" -#include "config.h" -#include "spindle_control.h" -#include "motion_control.h" -#include "planner.h" #include "protocol.h" +#include "planner.h" +#include "stepper.h" +#include "motion_control.h" #include "limits.h" #include "report.h" @@ -42,7 +36,7 @@ void limits_init() if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down. } else { - LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. + LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. } if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { @@ -88,11 +82,9 @@ void limits_disable() // limit setting if their limits are constantly triggering after a reset and move their axes. if (sys.state != STATE_ALARM) { if (bit_isfalse(sys.execute,EXEC_ALARM)) { - #ifndef LIMIT_SWITCHES_ACTIVE_HIGH - if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) { - #else - if (LIMIT_PIN & LIMIT_MASK) { - #endif + uint8_t bits = LIMIT_PIN; + if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { bits ^= LIMIT_MASK; } + if (bits & LIMIT_MASK) { mc_reset(); // Initiate system kill. sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event } @@ -151,9 +143,9 @@ void limits_go_home(uint8_t cycle_mask, bool approach, float homing_rate) target[i] = 0.0; } } - if (bit_istrue(settings.homing_dir_mask,(1<. */ -/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has - been integral throughout the development of the higher level details of Grbl, as well - as being a consistent sounding board for the future of accessible and free CNC. */ - -#include -#include "config.h" +#include "system.h" +#include "serial.h" +#include "settings.h" +#include "protocol.h" +#include "gcode.h" #include "planner.h" -#include "nuts_bolts.h" #include "stepper.h" #include "spindle_control.h" #include "coolant_control.h" #include "motion_control.h" -#include "gcode.h" -#include "protocol.h" #include "limits.h" #include "report.h" -#include "settings.h" -#include "serial.h" + // Declare system global variable structure system_t sys; + int main(void) { - // Initialize system - serial_init(); // Setup serial baud rate and interrupts + // Initialize system upon power-up. + serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load grbl settings from EEPROM - st_init(); // Setup stepper pins and interrupt timers - sei(); // Enable interrupts + stepper_init(); // Configure stepper pins and interrupt timers + system_init(); // Configure pinout pins and pin-change interrupt + sei(); memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization @@ -65,49 +62,29 @@ int main(void) 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 - 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(); - plan_reset(); // Clear block buffer and planner variables - st_reset(); // Clear stepper subsystem variables. + // Reset the system primary functionality. + serial_reset_read_buffer(); // Clear serial read buffer + gc_init(); // Set g-code parser to default state + spindle_init(); + coolant_init(); + limits_init(); + plan_reset(); // Clear block buffer and planner variables + 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. - plan_sync_position(); - gc_sync_position(); + // Sync cleared gcode and planner positions to current system position. + plan_sync_position(); + gc_sync_position(); - // Reset system variables. - sys.abort = false; - sys.execute = 0; - if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } - else { sys.auto_start = false; } - - // 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; // Clear all state flags. - protocol_execute_startup(); - } - } - - protocol_execute_runtime(); - - // 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. - mc_auto_cycle_start(); - protocol_process(); // ... process the serial protocol + // Reset system variables. + sys.abort = false; + sys.execute = 0; + if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } + else { sys.auto_start = false; } + + // Start main loop. Processes inputs and executes them. + // NOTE: Upon a system abort, the main loop returns and re-initializes the system. + protocol_process(); } - return 0; /* never reached */ + return 0; /* Never reached */ } diff --git a/motion_control.c b/motion_control.c index e30274a..ee1798b 100644 --- a/motion_control.c +++ b/motion_control.c @@ -20,21 +20,17 @@ along with Grbl. If not, see . */ -#include -#include -#include -#include +#include "system.h" #include "settings.h" -#include "config.h" +#include "protocol.h" #include "gcode.h" +#include "planner.h" +#include "stepper.h" #include "motion_control.h" #include "spindle_control.h" #include "coolant_control.h" -#include "nuts_bolts.h" -#include "stepper.h" -#include "planner.h" #include "limits.h" -#include "protocol.h" + // 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 @@ -112,8 +108,8 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 // Computes: mm_per_arc_segment = sqrt(4*arc_tolerance*(2*radius-arc_tolerance)), // segments = millimeters_of_travel/mm_per_arc_segment float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel)); - uint16_t segments = floor(millimeters_of_travel/ - sqrt(4*settings.arc_tolerance*(2*radius - settings.arc_tolerance)) ); + uint16_t segments = floor(0.5*millimeters_of_travel/ + sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) ); if (segments) { // Multiply inverse feed_rate to compensate for the fact that this movement is approximated @@ -123,7 +119,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8 float theta_per_segment = angular_travel/segments; float linear_per_segment = linear_travel/segments; - + /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, and phi is the angle of rotation. Solution approach by Jens Geisler. r_T = [cos(phi) -sin(phi); diff --git a/motion_control.h b/motion_control.h index a5e0371..b17e168 100644 --- a/motion_control.h +++ b/motion_control.h @@ -22,8 +22,6 @@ #ifndef motion_control_h #define motion_control_h -#include -#include "planner.h" // 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 diff --git a/nuts_bolts.c b/nuts_bolts.c index 6914052..57150ef 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -19,10 +19,8 @@ along with Grbl. If not, see . */ -#include -#include "nuts_bolts.h" -#include "gcode.h" -#include "planner.h" +#include "system.h" + #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) diff --git a/nuts_bolts.h b/nuts_bolts.h index 5140575..f9f08dd 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -22,13 +22,6 @@ #ifndef nuts_bolts_h #define nuts_bolts_h -#include -#include -#include -#include "config.h" -#include "defaults.h" -#include "cpu_map.h" - #define false 0 #define true 1 @@ -57,44 +50,6 @@ #define bit_istrue(x,mask) ((x & mask) != 0) #define bit_isfalse(x,mask) ((x & mask) == 0) -// Define system executor bit map. Used internally by runtime protocol as runtime command flags, -// which notifies the main program to execute the specified runtime command asynchronously. -// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default -// flags are always false, so the runtime protocol only needs to check for a non-zero value to -// know when there is a runtime command to execute. -#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 -#define EXEC_CYCLE_START bit(1) // bitmask 00000010 -#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_ALARM bit(5) // bitmask 00100000 -#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 -// #define bit(7) // bitmask 10000000 - -// Define system state bit map. The state variable primarily tracks the individual functions -// of Grbl to manage each without overlapping. It is also used as a messaging flag for -// critical events. -#define STATE_IDLE 0 // Must be zero. No flags. -#define STATE_QUEUED bit(0) // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE bit(1) // Cycle is running -#define STATE_HOLD bit(2) // Executing feed hold -#define STATE_HOMING bit(3) // Performing homing cycle -#define STATE_ALARM bit(4) // In alarm state. Locks out all g-code processes. Allows settings access. -#define STATE_CHECK_MODE bit(5) // G-code check mode. Locks out planner and motion only. -// #define STATE_JOG bit(6) // Jogging mode is unique like homing. - -// 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. - volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. - uint8_t homing_axis_lock; - 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. -} system_t; -extern system_t sys; - // Read a floating point value from a string. Line points to the input buffer, char_counter // is the indexer pointing to the current character of the line, while float_ptr is // a pointer to the result variable. Returns true when it succeeds diff --git a/planner.c b/planner.c index 143837a..9e435c7 100644 --- a/planner.c +++ b/planner.c @@ -22,14 +22,12 @@ /* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */ -#include -#include +#include "system.h" #include "planner.h" -#include "nuts_bolts.h" +#include "protocol.h" #include "stepper.h" #include "settings.h" -#include "config.h" -#include "protocol.h" + #define SOME_LARGE_VALUE 1.0E+38 // Used by rapids and acceleration maximization calculations. Just needs // to be larger than any feasible (mm/min)^2 or mm/sec^2 value. diff --git a/planner.h b/planner.h index 1cc4309..99298ed 100644 --- a/planner.h +++ b/planner.h @@ -21,7 +21,8 @@ #ifndef planner_h #define planner_h -#include "nuts_bolts.h" + +#include "system.h" // The number of linear motions that can be in the plan at any give time #ifndef BLOCK_BUFFER_SIZE diff --git a/print.c b/print.c index 0e2f9b8..03e261d 100644 --- a/print.c +++ b/print.c @@ -22,12 +22,11 @@ /* This code was initially inspired by the wiring_serial module by David A. Mellis which used to be a part of the Arduino project. */ - -#include -#include "config.h" +#include "system.h" #include "serial.h" #include "settings.h" + void printString(const char *s) { while (*s) diff --git a/protocol.c b/protocol.c index 54c959f..ade1df0 100644 --- a/protocol.c +++ b/protocol.c @@ -1,5 +1,5 @@ /* - protocol.c - the serial protocol master control unit + protocol.c - controls Grbl execution procedures Part of Grbl Copyright (c) 2011-2014 Sungeun K. Jeon @@ -19,78 +19,19 @@ along with Grbl. If not, see . */ -#include -#include +#include "system.h" +#include "serial.h" +#include "settings.h" #include "protocol.h" #include "gcode.h" -#include "serial.h" -#include "print.h" -#include "settings.h" -#include "config.h" -#include "nuts_bolts.h" #include "stepper.h" -#include "report.h" #include "motion_control.h" +#include "report.h" + static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. -static uint8_t char_counter; // Last character counter in line variable. -static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters. -static void protocol_reset_line_buffer() -{ - char_counter = 0; - iscomment = false; -} - - -void protocol_init() -{ - protocol_reset_line_buffer(); // Reset line input - report_init_message(); // Welcome message - - PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins - PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation. - PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt - PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt -} - - -// Executes user startup script, if stored. -void protocol_execute_startup() -{ - uint8_t n; - for (n=0; n < N_STARTUP_LINE; n++) { - if (!(settings_read_startup_line(n, line))) { - report_status_message(STATUS_SETTING_READ_FAIL); - } else { - if (line[0] != 0) { - printString(line); // Echo startup line to indicate execution. - report_status_message(gc_execute_line(line)); - } - } - } -} - - -// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets -// only the runtime command execute variable to have the main program execute these when -// its ready. This works exactly like the character-based runtime commands when picked off -// directly from the incoming serial data stream. -ISR(PINOUT_INT_vect) -{ - // Enter only if any pinout pin is actively low. - if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { - if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { - mc_reset(); - } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { - sys.execute |= EXEC_FEED_HOLD; - } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { - sys.execute |= EXEC_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. @@ -181,188 +122,111 @@ void protocol_execute_runtime() // Directs and executes one line of formatted input from protocol_process. While mostly -// incoming streaming g-code blocks, this also executes Grbl internal commands, such as -// settings, initiating the homing cycle, and toggling switch states. This differs from -// the runtime command module by being susceptible to when Grbl is ready to execute the -// next line during a cycle, so for switches like block delete, the switch only effects -// the lines that are processed afterward, not necessarily real-time during a cycle, -// since there are motions already stored in the buffer. However, this 'lag' should not -// be an issue, since these commands are not typically used during a cycle. -uint8_t protocol_execute_line(char *line) -{ - // Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help - if(line[0] == '$') { - uint8_t char_counter = 1; - uint8_t helper_var = 0; // Helper variable - float parameter, value; - switch( line[char_counter] ) { - case '#' : // Print gcode parameters - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_gcode_parameters(); } - break; - case 'G' : // Prints gcode parser state - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_gcode_modes(); } - break; -// case 'J' : break; // Jogging methods - // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be - // susceptible to other runtime commands except for e-stop. The jogging function is intended to - // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped - // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would - // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the - // motion by repeatedly toggling to slow the motion to the desired location. Location data would - // need to be updated real-time and supplied to the user through status queries. - // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are - // handled by the planner. It would be possible for the jog subprogram to insert blocks into the - // block buffer without having the planner plan them. It would need to manage de/ac-celerations - // on its own carefully. This approach could be effective and possibly size/memory efficient. - default : - // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing) - if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); } - switch( line[char_counter] ) { - case 0 : report_grbl_help(); break; - case '$' : // Prints Grbl settings - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - else { report_grbl_settings(); } - break; - case 'C' : // Set check g-code mode - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - // Perform reset when toggling off. Check g-code mode should only work if Grbl - // is idle and ready, regardless of alarm locks. This is mainly to keep things - // simple and consistent. - if ( sys.state == STATE_CHECK_MODE ) { - mc_reset(); - report_feedback_message(MESSAGE_DISABLED); - } else { - if (sys.state) { return(STATUS_IDLE_ERROR); } - sys.state = STATE_CHECK_MODE; - report_feedback_message(MESSAGE_ENABLED); - } - break; - case 'X' : // Disable alarm lock - if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } - if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_ALARM_UNLOCK); - sys.state = STATE_IDLE; - // Don't run startup script. Prevents stored moves in startup from causing accidents. - } - break; - case 'H' : // Perform homing cycle - if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - // Only perform homing if Grbl is idle or alarm. - mc_homing_cycle(); - if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing. - } else { return(STATUS_SETTING_DISABLED); } - break; - case 'I' : // Print or store build info. - if ( line[++char_counter] == 0 ) { - if (!(settings_read_build_info(line))) { - report_status_message(STATUS_SETTING_READ_FAIL); - } else { - report_build_info(line); - } - } else { // Store startup line - if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } - helper_var = char_counter; // Set helper variable as counter to start of user info line. - do { - line[char_counter-helper_var] = line[char_counter]; - } while (line[char_counter++] != 0); - settings_store_build_info(line); - } - break; - case 'N' : // Startup lines. - if ( line[++char_counter] == 0 ) { // Print startup lines - for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { - if (!(settings_read_startup_line(helper_var, line))) { - report_status_message(STATUS_SETTING_READ_FAIL); - } else { - report_startup_line(helper_var,line); - } - } - break; - } else { // Store startup line - helper_var = true; // Set helper_var to flag storing method. - // No break. Continues into default: to read remaining command characters. - } - default : // Storing setting methods - if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } - if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } - if (helper_var) { // Store startup line - // Prepare sending gcode block to gcode parser by shifting all characters - helper_var = char_counter; // Set helper variable as counter to start of gcode block - do { - line[char_counter-helper_var] = line[char_counter]; - } while (line[char_counter++] != 0); - // Execute gcode block to ensure block is valid. - helper_var = gc_execute_line(line); // Set helper_var to returned status code. - if (helper_var) { return(helper_var); } - else { - helper_var = trunc(parameter); // Set helper_var to int value of parameter - settings_store_startup_line(helper_var,line); - } - } else { // Store global setting. - if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } - if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } - return(settings_store_global_setting(parameter, value)); - } - } - } - return(STATUS_OK); // If '$' command makes it to here, then everything's ok. +// incoming streaming g-code blocks, this also directs and executes Grbl internal commands, +// such as settings, initiating the homing cycle, and toggling switch states. +// TODO: Eventually re-organize this function to more cleanly organize order of operations, +// which will hopefully reduce some of the current spaghetti logic and dynamic memory usage. +static void protocol_execute_line(char *line) +{ + protocol_execute_runtime(); // Runtime command check point. + if (sys.abort) { return; } // Bail to calling function upon system abort + uint8_t status; + if (line[0] == 0) { + // Empty or comment line. Send status message for syncing purposes. + status = STATUS_OK; + + } else if (line[0] == '$') { + // Grbl '$' system command + status = system_execute_line(line); + } else { - return(gc_execute_line(line)); // Everything else is gcode + // Everything else is gcode. Send to g-code parser! + // TODO: Separate the parsing from the g-code execution. Need to re-write the parser + // completely to do this. First parse the line completely, checking for modal group + // errors and storing all of the g-code words. Then, send the stored g-code words to + // a separate g-code executor. This will be more in-line with actual g-code protocol. + status = gc_execute_line(line); + } + + report_status_message(status); } -// Process and report status one line of incoming serial data. Performs an initial filtering -// by removing spaces and comments and capitalizing all letters. void protocol_process() { + // ------------------------------------------------------------ + // 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! + sys.state = STATE_IDLE; // Set system to ready. Clear all state flags. + system_execute_startup(line); // Execute startup script. + } + + // ------------------------------------------------------------------------------ + // Main loop! Upon a system abort, this exits back to main() to reset the system. + // ------------------------------------------------------------------------------ + + uint8_t iscomment = false; + uint8_t char_counter = 0; uint8_t c; - while((c = serial_read()) != SERIAL_NO_DATA) { - if ((c == '\n') || (c == '\r')) { // End of line reached + for (;;) { - // Runtime command check point before executing line. Prevent any furthur line executions. - // NOTE: If there is no line, this function should quickly return to the main program when - // the buffer empties of non-executable data. - protocol_execute_runtime(); - if (sys.abort) { return; } // Bail to main program upon system abort - - if (char_counter > 0) {// Line is complete. Then execute! - line[char_counter] = 0; // Terminate string - report_status_message(protocol_execute_line(line)); - } else { - // Empty or comment line. Skip block. - report_status_message(STATUS_OK); // Send status message for syncing purposes. - } - protocol_reset_line_buffer(); - - } else { - if (iscomment) { - // Throw away all comment characters - if (c == ')') { - // End of comment. Resume line. - iscomment = false; - } + // 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. + 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! + iscomment = false; + char_counter = 0; } else { - if (c <= ' ') { - // Throw away whitepace and control characters - } else if (c == '/') { - // Block delete not supported. Ignore character. - } else if (c == '(') { - // Enable comments flag and ignore all characters until ')' or EOL. - iscomment = true; - } else if (char_counter >= LINE_BUFFER_SIZE-1) { - // Detect line buffer overflow. Report error and reset line buffer. - report_status_message(STATUS_OVERFLOW); - protocol_reset_line_buffer(); - } else if (c >= 'a' && c <= 'z') { // Upcase lowercase - line[char_counter++] = c-'a'+'A'; + if (iscomment) { + // Throw away all comment characters + if (c == ')') { + // End of comment. Resume line. + iscomment = false; + } } else { - line[char_counter++] = c; + if (c <= ' ') { + // Throw away whitepace and control characters + } else if (c == '/') { + // Block delete not supported. Ignore character. + } else if (c == '(') { + // Enable comments flag and ignore all characters until ')' or EOL. + iscomment = true; + } else if (char_counter >= LINE_BUFFER_SIZE-1) { + // Detect line buffer overflow. Report error and reset line buffer. + report_status_message(STATUS_OVERFLOW); + iscomment = false; + char_counter = 0; + } else if (c >= 'a' && c <= 'z') { // Upcase lowercase + line[char_counter++] = c-'a'+'A'; + } else { + line[char_counter++] = c; + } } } } + + protocol_execute_runtime(); // Runtime command check point. + if (sys.abort) { return; } // Bail to main() program loop to reset system. + + // 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. + mc_auto_cycle_start(); + } + + return; /* Never reached */ } diff --git a/protocol.h b/protocol.h index b6267d4..9107d1d 100644 --- a/protocol.h +++ b/protocol.h @@ -1,5 +1,5 @@ /* - protocol.h - the serial protocol master control unit + protocol.h - controls Grbl execution procedures Part of Grbl Copyright (c) 2011-2014 Sungeun K. Jeon @@ -21,32 +21,21 @@ #ifndef protocol_h #define protocol_h -#include - // Line buffer size from the serial input stream to be executed. // NOTE: Not a problem except for extreme cases, but the line buffer size can be too small // and g-code blocks can get truncated. Officially, the g-code standards support up to 256 // characters. In future versions, this will be increased, when we know how much extra -// memory space we can invest into here or we re-write the g-code parser not to have his +// memory space we can invest into here or we re-write the g-code parser not to have this // buffer. #ifndef LINE_BUFFER_SIZE #define LINE_BUFFER_SIZE 70 #endif -// Initialize the serial protocol -void protocol_init(); - -// Read command lines from the serial port and execute them as they -// come in. Blocks until the serial buffer is emptied. -void protocol_process(); - -// Executes one line of input according to protocol -uint8_t protocol_execute_line(char *line); - // Checks and executes a runtime command at various stop points in main program void protocol_execute_runtime(); -// Execute the startup script lines stored in EEPROM upon initialization -void protocol_execute_startup(); +// Starts Grbl main loop. It handles all incoming characters from the serial port and executes +// them as they complete. It is also responsible for finishing the initialization procedures. +void protocol_process(); #endif diff --git a/report.c b/report.c index 1796c8c..ee4de61 100644 --- a/report.c +++ b/report.c @@ -26,11 +26,10 @@ methods to accomodate their needs. */ -#include +#include "system.h" #include "report.h" #include "print.h" #include "settings.h" -#include "nuts_bolts.h" #include "gcode.h" #include "coolant_control.h" @@ -163,9 +162,9 @@ void report_grbl_settings() { printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds); printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate); printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.step_invert_mask); - printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.step_invert_mask); + printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask); printPgmString(PSTR(")\r\n$15=")); printInteger(settings.dir_invert_mask); - printPgmString(PSTR(" (dir port invert mask, int:")); print_uint8_base2(settings.dir_invert_mask); + printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask); printPgmString(PSTR(")\r\n$16=")); printInteger(settings.stepper_idle_lock_time); printPgmString(PSTR(" (step idle delay, msec)\r\n$17=")); printFloat(settings.junction_deviation); printPgmString(PSTR(" (junction deviation, mm)\r\n$18=")); printFloat(settings.arc_tolerance); @@ -178,7 +177,7 @@ void report_grbl_settings() { printPgmString(PSTR(" (soft limits, bool)\r\n$25=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); printPgmString(PSTR(" (hard limits, bool)\r\n$26=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); printPgmString(PSTR(" (homing cycle, bool)\r\n$27=")); printInteger(settings.homing_dir_mask); - printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask); + printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask); printPgmString(PSTR(")\r\n$28=")); printFloat(settings.homing_feed_rate); printPgmString(PSTR(" (homing feed, mm/min)\r\n$29=")); printFloat(settings.homing_seek_rate); printPgmString(PSTR(" (homing seek, mm/min)\r\n$30=")); printInteger(settings.homing_debounce_delay); diff --git a/serial.c b/serial.c index 382ee41..29e361c 100644 --- a/serial.c +++ b/serial.c @@ -23,11 +23,12 @@ used to be a part of the Arduino project. */ #include +#include "system.h" #include "serial.h" -#include "config.h" #include "motion_control.h" #include "protocol.h" + uint8_t rx_buffer[RX_BUFFER_SIZE]; uint8_t rx_buffer_head = 0; volatile uint8_t rx_buffer_tail = 0; @@ -36,6 +37,7 @@ uint8_t tx_buffer[TX_BUFFER_SIZE]; uint8_t tx_buffer_head = 0; volatile uint8_t tx_buffer_tail = 0; + #ifdef ENABLE_XONXOFF volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable @@ -49,6 +51,7 @@ volatile uint8_t tx_buffer_tail = 0; } #endif + void serial_init() { // Set baud rate @@ -72,6 +75,7 @@ void serial_init() // defaults to 8-bit, no parity, 1 stop bit } + void serial_write(uint8_t data) { // Calculate next head uint8_t next_head = tx_buffer_head + 1; @@ -90,6 +94,7 @@ void serial_write(uint8_t data) { UCSR0B |= (1 << UDRIE0); } + // Data Register Empty Interrupt handler ISR(SERIAL_UDRE) { @@ -119,6 +124,7 @@ ISR(SERIAL_UDRE) if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } } + uint8_t serial_read() { uint8_t tail = rx_buffer_tail; // Temporary rx_buffer_tail (to optimize for volatile) @@ -142,6 +148,7 @@ uint8_t serial_read() } } + ISR(SERIAL_RX) { uint8_t data = UDR0; @@ -174,6 +181,7 @@ ISR(SERIAL_RX) } } + void serial_reset_read_buffer() { rx_buffer_tail = rx_buffer_head; diff --git a/serial.h b/serial.h index e768b44..74b429b 100644 --- a/serial.h +++ b/serial.h @@ -25,7 +25,6 @@ #ifndef serial_h #define serial_h -#include "nuts_bolts.h" #ifndef RX_BUFFER_SIZE #define RX_BUFFER_SIZE 128 diff --git a/settings.c b/settings.c index a312ee4..76306a3 100644 --- a/settings.c +++ b/settings.c @@ -19,29 +19,15 @@ along with Grbl. If not, see . */ -#include -#include "protocol.h" -#include "report.h" -#include "stepper.h" -#include "nuts_bolts.h" +#include "system.h" #include "settings.h" #include "eeprom.h" +#include "protocol.h" +#include "report.h" #include "limits.h" settings_t settings; -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[N_AXIS]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - // Method to store startup lines into EEPROM void settings_store_startup_line(uint8_t n, char *line) @@ -50,12 +36,14 @@ void settings_store_startup_line(uint8_t n, char *line) memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); } + // Method to store build info into EEPROM void settings_store_build_info(char *line) { memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE); } + // Method to store coord data parameters into EEPROM void settings_write_coord_data(uint8_t coord_select, float *coord_data) { @@ -63,6 +51,7 @@ void settings_write_coord_data(uint8_t coord_select, float *coord_data) memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); } + // Method to store Grbl global settings struct and version number into EEPROM void write_global_settings() { @@ -70,27 +59,24 @@ void write_global_settings() memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); } + // Method to reset Grbl global settings back to defaults. -void settings_reset(bool reset_all) { - // Reset all settings or only the migration settings to the new version. - if (reset_all) { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; - settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; - settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; - settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION; - settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION; - settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION; - settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; - settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } - // New settings since last version +void settings_reset() { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.default_feed_rate = DEFAULT_FEEDRATE; + settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; + settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; + settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; + settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION; + settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION; + settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION; + settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; + settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; settings.flags = 0; if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } @@ -112,13 +98,14 @@ void settings_reset(bool reset_all) { write_global_settings(); } + // Reads startup line from EEPROM. Updated pointed line string data. uint8_t settings_read_startup_line(uint8_t n, char *line) { uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { // Reset line with default value - line[0] = 0; + line[0] = 0; // Empty line settings_store_startup_line(n, line); return(false); } else { @@ -126,12 +113,13 @@ uint8_t settings_read_startup_line(uint8_t n, char *line) } } + // Reads startup line from EEPROM. Updated pointed line string data. uint8_t settings_read_build_info(char *line) { if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) { // Reset line with default value - line[0] = 0; + line[0] = 0; // Empty line settings_store_build_info(line); return(false); } else { @@ -139,6 +127,7 @@ uint8_t settings_read_build_info(char *line) } } + // Read selected coordinate data from EEPROM. Updates pointed coord_data value. uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) { @@ -153,26 +142,18 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) } } + // Reads Grbl global settings struct from EEPROM. uint8_t read_global_settings() { // Check version-byte of eeprom uint8_t version = eeprom_get_char(0); - if (version == SETTINGS_VERSION) { // Read settings-record and check checksum if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { return(false); } } else { - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { - return(false); - } - settings_reset(false); // Old settings ok. Write new settings only. - } else { - return(false); - } + return(false); } return(true); } @@ -180,9 +161,9 @@ uint8_t read_global_settings() { // A helper method to set settings from command line uint8_t settings_store_global_setting(int parameter, float value) { + if (value < 0.0) { return(STATUS_SETTING_VALUE_NEG); } switch(parameter) { case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } settings.steps_per_mm[parameter] = value; break; case 3: settings.max_rate[X_AXIS] = value; break; case 4: settings.max_rate[Y_AXIS] = value; break; @@ -249,11 +230,12 @@ uint8_t settings_store_global_setting(int parameter, float value) { return(STATUS_OK); } + // Initialize the config subsystem void settings_init() { if(!read_global_settings()) { report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); + settings_reset(); report_grbl_settings(); } // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. diff --git a/settings.h b/settings.h index 05840bb..6ff54be 100644 --- a/settings.h +++ b/settings.h @@ -22,15 +22,15 @@ #ifndef settings_h #define settings_h -#include -#include "nuts_bolts.h" +#include "system.h" + #define GRBL_VERSION "0.9c" -#define GRBL_VERSION_BUILD "20131231" +#define GRBL_VERSION_BUILD "20140110" // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // when firmware is upgraded. Always stored in byte 0 of eeprom -#define SETTINGS_VERSION 57 +#define SETTINGS_VERSION 6 // Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) diff --git a/spindle_control.c b/spindle_control.c index 70e819e..e97c3f7 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -19,17 +19,13 @@ along with Grbl. If not, see . */ -#include "settings.h" +#include "system.h" #include "spindle_control.h" #include "planner.h" -static uint8_t current_direction; -static uint16_t current_rpm; - void spindle_init() -{ - current_direction = 0; +{ SPINDLE_DIRECTION_DDR |= (1< 0) { - SPINDLE_DIRECTION_PORT &= ~(1< SPINDLE_MAX_RPM) { rpm = SPINDLE_MAX_RPM; } // Prevent overflow. - uint8_t current_pwm = floor((((float) rpm / (float) SPINDLE_MAX_RPM ) * 255.0) + 0.5); - OCR_REGISTER = current_pwm; - - #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared. - SPINDLE_ENABLE_PORT |= (1< 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< + +#define SPINDLE_DISABLE 0 // Must be zero. +#define SPINDLE_ENABLE_CW 1 +#define SPINDLE_ENABLE_CCW 2 + // Initializes spindle pins and hardware PWM, if enabled. void spindle_init(); // Sets spindle direction and spindle rpm via PWM, if enabled. -void spindle_run(int8_t direction, uint16_t rpm); +void spindle_run(uint8_t direction, float rpm); // Kills spindle. void spindle_stop(); diff --git a/stepper.c b/stepper.c index 7792896..4618ef8 100644 --- a/stepper.c +++ b/stepper.c @@ -19,12 +19,11 @@ along with Grbl. If not, see . */ -#include +#include "system.h" +#include "nuts_bolts.h" #include "stepper.h" -#include "config.h" #include "settings.h" #include "planner.h" -#include "nuts_bolts.h" // Some useful constants. @@ -43,7 +42,7 @@ // NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead // and timer accuracy. Do not alter these settings unless you know what you are doing. #define MAX_AMASS_LEVEL 3 -// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. +// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency. #define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz) #define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4) #define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8) @@ -182,12 +181,10 @@ static st_prep_t prep; // enabled. Startup init and limits call this function but shouldn't start the cycle. void st_wake_up() { - // Enable steppers by resetting the stepper disable port - if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { - STEPPERS_DISABLE_PORT |= (1<millimeters; float time_var = dt_max; // Time worker variable float mm_var; // mm-Distance worker variable @@ -676,8 +673,8 @@ void st_prep_buffer() switch (prep.ramp_type) { case RAMP_ACCEL: // NOTE: Acceleration ramp only computes during first do-while loop. - speed_var = pl_block->acceleration*dt_max; - mm_remaining -= dt_max*(prep.current_speed + 0.5*speed_var); + speed_var = pl_block->acceleration*time_var; + mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var); if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp. // Acceleration-cruise, acceleration-deceleration ramp junction, or end of block. mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB @@ -718,10 +715,14 @@ void st_prep_buffer() } dt += time_var; // Add computed ramp time to total segment time. if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction. - else if (mm_remaining > prep.minimum_mm) { // Check for slow segments with zero steps. - dt_max += DT_SEGMENT; // Increase segment time to ensure at least one step in segment. - time_var = dt_max - dt; - } else { break; } // **Complete** Exit loop. Segment execution time maxed. + else { + if (mm_remaining > prep.minimum_mm) { // Check for slow segments with zero steps. + dt_max += DT_SEGMENT; // Increase segment time to ensure at least one step in segment. + time_var = dt_max - dt; + } else { + break; // **Complete** Exit loop. Segment execution time maxed. + } + } } while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete. /* ----------------------------------------------------------------------------------- @@ -745,7 +746,7 @@ void st_prep_buffer() #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Compute step timing and multi-axis smoothing level. - // NOTE: Only one prescalar is required with AMASS enabled. + // NOTE: AMASS overdrives the timer with each level, so only one prescalar is required. if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; } else { if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; } diff --git a/stepper.h b/stepper.h index a72a565..fe9323f 100644 --- a/stepper.h +++ b/stepper.h @@ -27,7 +27,7 @@ #endif // Initialize and setup the stepper motor subsystem -void st_init(); +void stepper_init(); // Enable steppers, but cycle does not start unless called by motion control or runtime command. void st_wake_up(); @@ -47,7 +47,7 @@ void st_cycle_reinitialize(); // Initiates a feed hold of the running program void st_feed_hold(); -// Reloads step segment buffer. Called continuously by runtime execution protocol. +// Reloads step segment buffer. Called continuously by runtime execution system. void st_prep_buffer(); // Called by planner_recalculate() when the executing block is updated by the new plan. diff --git a/system.c b/system.c new file mode 100644 index 0000000..4dfe31d --- /dev/null +++ b/system.c @@ -0,0 +1,200 @@ +/* + system.c - Handles system level commands and real-time processes + Part of Grbl + + Copyright (c) 2014 Sungeun K. Jeon + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include "system.h" +#include "settings.h" +#include "gcode.h" +#include "motion_control.h" +#include "report.h" +#include "print.h" + + +void system_init() +{ + PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins + PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation. + PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt + PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt +} + + +// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets +// only the runtime command execute variable to have the main program execute these when +// its ready. This works exactly like the character-based runtime commands when picked off +// directly from the incoming serial data stream. +ISR(PINOUT_INT_vect) +{ + // Enter only if any pinout pin is actively low. + if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { + if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { + mc_reset(); + } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { + sys.execute |= EXEC_FEED_HOLD; + } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { + sys.execute |= EXEC_CYCLE_START; + } + } +} + + +// Executes user startup script, if stored. +void system_execute_startup(char *line) +{ + uint8_t n; + for (n=0; n < N_STARTUP_LINE; n++) { + if (!(settings_read_startup_line(n, line))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } else { + if (line[0] != 0) { + printString(line); // Echo startup line to indicate execution. + report_status_message(gc_execute_line(line)); + } + } + } +} + + +// Directs and executes one line of formatted input from protocol_process. While mostly +// incoming streaming g-code blocks, this also executes Grbl internal commands, such as +// settings, initiating the homing cycle, and toggling switch states. This differs from +// the runtime command module by being susceptible to when Grbl is ready to execute the +// next line during a cycle, so for switches like block delete, the switch only effects +// the lines that are processed afterward, not necessarily real-time during a cycle, +// since there are motions already stored in the buffer. However, this 'lag' should not +// be an issue, since these commands are not typically used during a cycle. +uint8_t system_execute_line(char *line) +{ + uint8_t char_counter = 1; + uint8_t helper_var = 0; // Helper variable + float parameter, value; + switch( line[char_counter] ) { + case '#' : // Print gcode parameters + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_gcode_parameters(); } + break; + case 'G' : // Prints gcode parser state + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_gcode_modes(); } + break; +// case 'J' : break; // Jogging methods + // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be + // susceptible to other runtime commands except for e-stop. The jogging function is intended to + // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped + // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would + // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the + // motion by repeatedly toggling to slow the motion to the desired location. Location data would + // need to be updated real-time and supplied to the user through status queries. + // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are + // handled by the planner. It would be possible for the jog subprogram to insert blocks into the + // block buffer without having the planner plan them. It would need to manage de/ac-celerations + // on its own carefully. This approach could be effective and possibly size/memory efficient. + default : + // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing) + if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); } + switch( line[char_counter] ) { + case 0 : report_grbl_help(); break; + case '$' : // Prints Grbl settings + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + else { report_grbl_settings(); } + break; + case 'C' : // Set check g-code mode + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + // Perform reset when toggling off. Check g-code mode should only work if Grbl + // is idle and ready, regardless of alarm locks. This is mainly to keep things + // simple and consistent. + if ( sys.state == STATE_CHECK_MODE ) { + mc_reset(); + report_feedback_message(MESSAGE_DISABLED); + } else { + if (sys.state) { return(STATUS_IDLE_ERROR); } + sys.state = STATE_CHECK_MODE; + report_feedback_message(MESSAGE_ENABLED); + } + break; + case 'X' : // Disable alarm lock + if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_ALARM_UNLOCK); + sys.state = STATE_IDLE; + // Don't run startup script. Prevents stored moves in startup from causing accidents. + } + break; + case 'H' : // Perform homing cycle + if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { + // Only perform homing if Grbl is idle or lost. + mc_homing_cycle(); + if (!sys.abort) { system_execute_startup(line); } // Execute startup scripts after successful homing. + } else { return(STATUS_SETTING_DISABLED); } + break; + case 'I' : // Print or store build info. + if ( line[++char_counter] == 0 ) { + if (!(settings_read_build_info(line))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } else { + report_build_info(line); + } + } else { // Store startup line + if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } + helper_var = char_counter; // Set helper variable as counter to start of user info line. + do { + line[char_counter-helper_var] = line[char_counter]; + } while (line[char_counter++] != 0); + settings_store_build_info(line); + } + break; + case 'N' : // Startup lines. + if ( line[++char_counter] == 0 ) { // Print startup lines + for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { + if (!(settings_read_startup_line(helper_var, line))) { + report_status_message(STATUS_SETTING_READ_FAIL); + } else { + report_startup_line(helper_var,line); + } + } + break; + } else { // Store startup line + helper_var = true; // Set helper_var to flag storing method. + // No break. Continues into default: to read remaining command characters. + } + default : // Storing setting methods + if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } + if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } + if (helper_var) { // Store startup line + // Prepare sending gcode block to gcode parser by shifting all characters + helper_var = char_counter; // Set helper variable as counter to start of gcode block + do { + line[char_counter-helper_var] = line[char_counter]; + } while (line[char_counter++] != 0); + // Execute gcode block to ensure block is valid. + helper_var = gc_execute_line(line); // Set helper_var to returned status code. + if (helper_var) { return(helper_var); } + else { + helper_var = trunc(parameter); // Set helper_var to int value of parameter + settings_store_startup_line(helper_var,line); + } + } else { // Store global setting. + if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } + if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } + return(settings_store_global_setting(parameter, value)); + } + } + } + return(STATUS_OK); // If '$' command makes it to here, then everything's ok. +} diff --git a/system.h b/system.h new file mode 100644 index 0000000..78faa5c --- /dev/null +++ b/system.h @@ -0,0 +1,95 @@ +/* + system.h - Header for system level commands and real-time processes + Part of Grbl + + Copyright (c) 2014 Sungeun K. Jeon + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef system_h +#define system_h + +// Define system header files and standard libraries used by Grbl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Define Grbl configuration and shared header files +#include "config.h" +#include "defaults.h" +#include "cpu_map.h" +#include "nuts_bolts.h" + + +// Define system executor bit map. Used internally by runtime protocol as runtime command flags, +// which notifies the main program to execute the specified runtime command asynchronously. +// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default +// flags are always false, so the runtime protocol only needs to check for a non-zero value to +// know when there is a runtime command to execute. +#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 +#define EXEC_CYCLE_START bit(1) // bitmask 00000010 +#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_ALARM bit(5) // bitmask 00100000 +#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 +// #define bit(7) // bitmask 10000000 + +// Define system state bit map. The state variable primarily tracks the individual functions +// of Grbl to manage each without overlapping. It is also used as a messaging flag for +// critical events. +#define STATE_IDLE 0 // Must be zero. No flags. +#define STATE_QUEUED bit(0) // Indicates buffered blocks, awaiting cycle start. +#define STATE_CYCLE bit(1) // Cycle is running +#define STATE_HOLD bit(2) // Executing feed hold +#define STATE_HOMING bit(3) // Performing homing cycle +#define STATE_ALARM bit(4) // In alarm state. Locks out all g-code processes. Allows settings access. +#define STATE_CHECK_MODE bit(5) // G-code check mode. Locks out planner and motion only. +// #define STATE_JOG bit(6) // Jogging mode is unique like homing. + +// 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. + volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. + uint8_t homing_axis_lock; + 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. +} system_t; +extern system_t sys; + + +// Initialize the serial protocol +void system_init(); + +// Executes an internal system command, defined as a string starting with a '$' +uint8_t system_execute_line(char *line); + +// Checks and executes a runtime command at various stop points in main program +void system_execute_runtime(); + +// Execute the startup script lines stored in EEPROM upon initialization +void system_execute_startup(char *line); + +#endif