From cc9afdc195da0b2e71eda71ea24ddbd14378ceb1 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Fri, 10 Jan 2014 20:22:10 -0700 Subject: [PATCH] Lots of re-organization and cleaning-up. Some bug fixes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added a new source and header file called system. These files contain the system commands and variables, as well as all of the system headers and standard libraries Grbl uses. Centralizing some of the code. - Re-organized the include headers throughout the source code. - ENABLE_M7 define was missing from config.h. Now there. - SPINDLE_MAX_RPM and SPINDLE_MIN_RPM now defined in config.h. No uncommenting to prevent user issues. Minimum spindle RPM now provides the lower, near 0V, scale adjustment, i.e. some spindles can go really slow so why use up our 256 voltage bins for them? - Remove some persistent variables from coolant and spindle control. They were redundant. - Removed a VARIABLE_SPINDLE define in cpu_map.h that shouldn’t have been there. - Changed the DEFAULT_ARC_TOLERANCE to 0.002mm to improve arc tracing. Before we had issues with performance, no longer. - Fixed a bug with the hard limits and the software debounce feature enabled. The invert limit pin setting wasn’t honored. - Fixed a bug with the homing direction mask. Now is like it used to be. At least for now. - Re-organized main.c to serve as only as the reset/initialization routine. Makes things a little bit clearer in terms of execution procedures. - Re-organized protocol.c as the overall master control unit for execution procedures. Not quite there yet, but starting to make a little more sense in how things are run. - Removed updating of old settings records. So many new settings have been added that it’s not worth adding the code to migrate old user settings. - Tweaked spindle_control.c a bit and made it more clear and consistent with other parts of Grbl. - Tweaked the stepper disable bit code in stepper.c. Requires less flash memory. --- Makefile | 2 +- config.h | 27 +++- coolant_control.c | 24 +--- coolant_control.h | 2 +- cpu_map.h | 3 - defaults.h | 12 +- gcode.c | 17 +-- gcode.h | 10 +- limits.c | 42 +++--- limits.h | 1 + main.c | 89 +++++-------- motion_control.c | 20 ++- motion_control.h | 2 - nuts_bolts.c | 6 +- nuts_bolts.h | 45 ------- planner.c | 8 +- planner.h | 3 +- print.c | 5 +- protocol.c | 330 ++++++++++++++-------------------------------- protocol.h | 21 +-- report.c | 9 +- serial.c | 10 +- serial.h | 1 - settings.c | 84 +++++------- settings.h | 8 +- spindle_control.c | 64 ++++----- spindle_control.h | 8 +- stepper.c | 67 +++++----- stepper.h | 4 +- system.c | 200 ++++++++++++++++++++++++++++ system.h | 95 +++++++++++++ 31 files changed, 636 insertions(+), 583 deletions(-) create mode 100644 system.c create mode 100644 system.h 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