From 9df29ad3b3fb769317d9c5560c54e89c92a71680 Mon Sep 17 00:00:00 2001 From: Simen Svale Skogsrud Date: Sun, 25 Jan 2009 00:48:56 +0100 Subject: [PATCH] version 0.1 --- COPYING.txt | 165 ++++++++++++++++++++++ Makefile | 89 ++++++++++++ config.h | 73 ++++++++++ gcode.c | 308 ++++++++++++++++++++++++++++++++++++++++++ gcode.h | 41 ++++++ main.c | 41 ++++++ motion_control.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++ motion_control.h | 49 +++++++ nuts_bolts.h | 41 ++++++ readme.txt | 15 ++ serial_protocol.c | 89 ++++++++++++ serial_protocol.h | 28 ++++ spindle_control.c | 44 ++++++ spindle_control.h | 30 ++++ todo.txt | 7 + wiring_private.h | 56 ++++++++ wiring_serial.c | 212 +++++++++++++++++++++++++++++ wiring_serial.h | 43 ++++++ 18 files changed, 1669 insertions(+) create mode 100644 COPYING.txt create mode 100644 Makefile create mode 100644 config.h create mode 100644 gcode.c create mode 100644 gcode.h create mode 100644 main.c create mode 100644 motion_control.c create mode 100644 motion_control.h create mode 100644 nuts_bolts.h create mode 100644 readme.txt create mode 100644 serial_protocol.c create mode 100644 serial_protocol.h create mode 100644 spindle_control.c create mode 100644 spindle_control.h create mode 100644 todo.txt create mode 100644 wiring_private.h create mode 100644 wiring_serial.c create mode 100644 wiring_serial.h diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..3f9959f --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1befab1 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# Part of Grbl +# +# Copyright (c) 2009 Simen Svale Skogsrud +# +# 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 . + + +# This is a prototype Makefile. Modify it according to your needs. +# You should at least check the settings for +# DEVICE ....... The AVR device you compile for +# CLOCK ........ Target AVR clock rate in Hertz +# OBJECTS ...... The object files created from your source files. This list is +# usually the same as the list of source files with suffix ".o". +# PROGRAMMER ... Options to avrdude which define the hardware you use for +# uploading to the AVR and the interface where this hardware +# is connected. +# FUSES ........ Parameters for avrdude to flash the fuses appropriately. + +DEVICE = atmega168 +CLOCK = 20000000 +PROGRAMMER = -c avrisp2 -P usb +OBJECTS = main.o motion_control.o gcode.o spindle_control.o wiring_serial.o serial_protocol.o +FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m + +# Tune the lines below only if you know what you are doing: + +AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. + +# symbolic targets: +all: main.hex + +.c.o: + $(COMPILE) -c $< -o $@ + +.S.o: + $(COMPILE) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +.c.s: + $(COMPILE) -S $< -o $@ + +flash: all + $(AVRDUDE) -U flash:w:main.hex:i + +fuse: + $(AVRDUDE) $(FUSES) + +# Xcode uses the Makefile targets "", "clean" and "install" +install: flash fuse + +# if you use a bootloader, change the command below appropriately: +load: all + bootloadHID main.hex + +clean: + rm -f main.hex main.elf $(OBJECTS) + +# file targets: +main.elf: $(OBJECTS) + $(COMPILE) -o main.elf $(OBJECTS) -lm + +main.hex: main.elf + rm -f main.hex + avr-objcopy -j .text -j .data -O ihex main.elf main.hex + avr-size main.hex *.o +# If you have an EEPROM section, you must also create a hex file for the +# EEPROM and add it to the "flash" target. + +# Targets for code debugging and analysis: +disasm: main.elf + avr-objdump -d main.elf + +cpp: + $(COMPILE) -E main.c diff --git a/config.h b/config.h new file mode 100644 index 0000000..a103e98 --- /dev/null +++ b/config.h @@ -0,0 +1,73 @@ +/* + config.h - configuration data for Grbl + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 config_h +#define config_h + +#define VERSION "0.1" + +#define X_STEPS_PER_MM 100.0 +#define Y_STEPS_PER_MM 100.0 +#define Z_STEPS_PER_MM 100.0 + +#define INCHES_PER_MM 25.4 +#define X_STEPS_PER_INCH X_STEPS_PER_MM*INCHES_PER_MM +#define Y_STEPS_PER_INCH Y_STEPS_PER_MM*INCHES_PER_MM +#define Z_STEPS_PER_INCH Z_STEPS_PER_MM*INCHES_PER_MM + +#define RAPID_FEEDRATE 1270.0 // in millimeters per minute +#define DEFAULT_FEEDRATE 635.0 + +#define STEPPERS_ENABLE_DDR DDRB +#define STEPPERS_ENABLE_PORT PORTB +#define STEPPERS_ENABLE_BIT 6 + +#define STEP_DDR DDRB +#define STEP_PORT PORTB +#define X_STEP_BIT 0 +#define Y_STEP_BIT 2 +#define Z_STEP_BIT 4 +#define STEP_MASK (1<. +*/ + +/* This code is inspired by the Arduino GCode Interpreter by Mike Ellery and the NIST RS274/NGC Interpreter + by Kramer, Proctor and Messina. */ + +/* Intentionally not supported: + - Canned cycles + - Tool radius compensatino + - A,B,C-axes + - Multiple coordinate systems + - Evaluation of expressions + - Variables + - Multiple home locations + - Probing + - Spindle direction + - Override control +*/ + +/* + Omitted for the time being: + + group 0 = {G10, G28, G30, G53, G92, G92.1, G92.2, G92.3} (Non modal G-codes) + group 5 = {G93, G94} feed rate mode + group 12 = {G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection + group 13 = {G61, G61.1, G64} path control mode + group 4 = {M0, M1, M2, M30, M60} stopping + group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time) + group 9 = {M48, M49} enable/disable feed and speed override switches +*/ + +#include "gcode.h" +#include +#include +#include "nuts_bolts.h" +#include +#include "config.h" +#include "motion_control.h" +#include "spindle_control.h" + +#define NEXT_ACTION_DEFAULT 0 +#define NEXT_ACTION_DWELL 1 +#define NEXT_ACTION_GO_HOME 2 + +#define MOTION_MODE_RAPID_LINEAR 0 // G0 +#define MOTION_MODE_LINEAR 1 // G1 +#define MOTION_MODE_CW_ARC 2 // G2 +#define MOTION_MODE_CCW_ARC 3 // G3 +#define MOTION_MODE_CANCEL 4 // G80 + +#define PLANE_XY 0; // G17 +#define PLANE_XZ 1; // G18 +#define PLANE_YZ 2; // G19 + +#define PATH_CONTROL_MODE_EXACT_PATH 0 +#define PATH_CONTROL_MODE_EXACT_STOP 1 +#define PATH_CONTROL_MODE_CONTINOUS 2 + +#define PROGRAM_FLOW_RUNNING 0 +#define PROGRAM_FLOW_PAUSED 1 +#define PROGRAM_FLOW_COMPLETED 2 + +#define SPINDLE_DIRECTION_CW 0 +#define SPINDLE_DIRECTION_CCW 1 + +// Using packed bit fields saves a "lot" of invaluable SRAM, but bumps the compiled size of this unit +// by 100 bytes. If we get tight on code space, consider using byte aligned values again. +struct ParserState { + uint32_t line_number; + uint8_t status_code:5; + + uint8_t motion_mode:3; /* {G0, G1, G2, G3, G38.2, G80, G81, G82, G83, G84, G85, G86, G87, G88, G89} */ + uint8_t inverse_feed_rate_mode:1; /* G93, G94 */ + uint8_t plane:2; /* {G17, G18, G19} */ + uint8_t inches_mode:1; /* 0 = millimeter mode, 1 = inches mode {G20, G21} */ + uint8_t program_flow:2; + int spindle_direction:2; + double feed_rate; /* Millimeters/second */ + double logical_position[3]; /* Where the interpreter considers the tool to be at this point in the code */ + uint8_t tool; + int16_t spindle_speed; /* RPM/100 */ +}; +struct ParserState state; + +#define FAIL(status) state.status_code = status; + +int read_double(char *line, //!< string: line of RS274/NGC code being processed + int *counter, //!< pointer to a counter for logical_position on the line + double *double_ptr); //!< pointer to double to be read + +int next_statement(char *letter, double *double_ptr, char *line, int *counter); + + +void gc_init() { + memset(&state, 0, sizeof(state)); + state.feed_rate = DEFAULT_FEEDRATE; +} + +inline float to_millimeters(double value) { + return(state.inches_mode ? value * INCHES_PER_MM : value); +} + +// Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase +// characters and signed floats (no whitespace). +uint8_t gc_execute_line(char *line) { + int counter; + char letter; + double value; + double unit_converted_value; + double inverse_feed_rate; + + uint8_t absolute_mode = 0; /* 0 = relative motion, 1 = absolute motion {G90, G91} */ + uint8_t next_action = NEXT_ACTION_DEFAULT; /* One of the NEXT_ACTION_-constants */ + + double target[3], offset[3]; + + double p, r; + int int_value, axis; + + state.line_number++; + state.status_code = GCSTATUS_OK; + + /* First: parse all statements */ + + if (line[0] == '(') { return(state.status_code); } + if (line[0] == '/') { counter++; } // ignore block delete + + // Pass 1: Commands + while(next_statement(&letter, &value, line, &counter)) { + int_value = trunc(value); + switch(letter) { + case 'G': + switch(int_value) { + case 0: state.motion_mode = MOTION_MODE_RAPID_LINEAR; break; + case 1: state.motion_mode = MOTION_MODE_LINEAR; break; + case 2: state.motion_mode = MOTION_MODE_CW_ARC; break; + case 3: state.motion_mode = MOTION_MODE_CCW_ARC; break; + case 4: next_action = NEXT_ACTION_DWELL; break; + case 17: state.plane = PLANE_XY; break; + case 18: state.plane = PLANE_XZ; break; + case 19: state.plane = PLANE_YZ; break; + case 20: state.inches_mode = true; break; + case 21: state.inches_mode = false; break; + case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break; + case 53: absolute_mode = 1; break; + case 80: state.motion_mode = MOTION_MODE_CANCEL; break; + case 93: state.inverse_feed_rate_mode = true; break; + case 94: state.inverse_feed_rate_mode = false; break; + default: FAIL(GCSTATUS_UNSUPPORTED_STATEMENT); + } + break; + + case 'M': + switch(int_value) { + case 0: case 1: state.program_flow = PROGRAM_FLOW_PAUSED; break; + case 2: state.program_flow = PROGRAM_FLOW_COMPLETED; break; + case 3: state.spindle_direction = 1; break; + case 4: state.spindle_direction = -1; break; + case 5: state.spindle_direction = 0; break; + default: FAIL(GCSTATUS_UNSUPPORTED_STATEMENT); + } + break; + case 'T': state.tool = trunc(value); break; + } + if(state.status_code) { break; } + } + + // If there were any errors parsing this line, we will return right away with the bad news + if (state.status_code) { return(state.status_code); } + + // Pass 2: Parameters + counter = 0; + clear_vector(offset); + while(next_statement(&letter, &value, line, &counter)) { + int_value = trunc(value); + unit_converted_value = to_millimeters(value); + switch(letter) { + case 'F': + if (state.inverse_feed_rate_mode) { + inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only + } else { + state.feed_rate = unit_converted_value; // millimeters pr second + } + break; + case 'I': case 'J': case 'K': offset[letter-'I'] = unit_converted_value; break; + case 'P': p = value; break; + case 'R': r = unit_converted_value; break; + case 'S': state.spindle_speed = value; break; + case 'X': case 'Y': case 'Z': + axis = letter - 'X'; + if (absolute_mode) { + target[axis] = unit_converted_value; + } else { + target[axis] = state.logical_position[axis]+unit_converted_value; + }; + break; + } + } + + // Update spindle state + if (state.spindle_direction) { + spindle_run(state.spindle_direction, state.spindle_speed); + } else { + spindle_stop(); + } + + // Perform any physical actions + switch (next_action) { + case NEXT_ACTION_GO_HOME: mc_go_home(); break; + case NEXT_ACTION_DWELL: mc_dwell(trunc(p*1000)); break; + case NEXT_ACTION_DEFAULT: + switch (state.motion_mode) { + case MOTION_MODE_CANCEL: break; + case MOTION_MODE_RAPID_LINEAR: case MOTION_MODE_LINEAR: + if (inverse_feed_rate) { + mc_linear_motion(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], + inverse_feed_rate, true); + } else { + mc_linear_motion(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], + (state.motion_mode == MOTION_MODE_LINEAR) ? state.feed_rate : RAPID_FEEDRATE, + false); + } + break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + // to be implemented + break; + } + } + + mc_execute(); + + // As far as the parser is concerned, the logical_position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + memcpy(state.logical_position, target, sizeof(state.logical_position)); + + return(state.status_code); +} + +void gc_get_status(double *position, uint8_t *status_code, int *inches_mode, uint32_t *line_number) +{ + int axis; + if (state.inches_mode) { + for(axis = X_AXIS; axis <= Z_AXIS; axis++) { + position[axis] = state.logical_position[axis]*INCHES_PER_MM; + } + } else { + memcpy(position, state.logical_position, sizeof(position)); + } + *status_code = state.status_code; + *inches_mode = state.inches_mode; + *line_number = state.line_number; +} + +// Parses the next statement and leaves the counter on the first character following +// the statement. Returns 1 if there was a statements, 0 if end of string was reached +// or there was an error (check state.status_code). +int next_statement(char *letter, double *double_ptr, char *line, int *counter) { + if (*line == 0) { + return(0); // No more statements + } + + *letter = *line; + if((*letter < 'A') || (*letter > 'Z')) { + FAIL(GCSTATUS_EXPECTED_COMMAND_LETTER); + return(0); + } + *counter++; + if (!read_double(line, counter, double_ptr)) { + return(0); + }; + return(1); +} + +int read_double(char *line, //!< string: line of RS274/NGC code being processed + int *counter, //!< pointer to a counter for position on the line + double *double_ptr) //!< pointer to double to be read +{ + char *start = line + *counter; + char *end; + + *double_ptr = strtod(start, &end); + if(end == start) { + FAIL(GCSTATUS_BAD_NUMBER_FORMAT); + return(0); + }; + + *counter = end - line; + return(1); +} + diff --git a/gcode.h b/gcode.h new file mode 100644 index 0000000..f22735a --- /dev/null +++ b/gcode.h @@ -0,0 +1,41 @@ +/* + gcode.c - rs274/ngc parser. + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 gcode_h +#define gcode_h +#include + +#define GCSTATUS_OK 0 +#define GCSTATUS_BAD_NUMBER_FORMAT 1 +#define GCSTATUS_EXPECTED_COMMAND_LETTER 2 +#define GCSTATUS_UNSUPPORTED_STATEMENT 3 +#define GCSTATUS_MOTION_CONTROL_ERROR 4 + +// Initialize the parser +void gc_init(); + +// Execute one block of rs275/ngc/g-code +uint8_t gc_execute_line(char *line); + +// get the current logical position (in current units), the current status code and the unit mode +void gc_get_status(double *position, uint8_t *status_code, int *inches_mode, uint32_t *line_number); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..d424bd6 --- /dev/null +++ b/main.c @@ -0,0 +1,41 @@ +/* + main.c - An embedded CNC Controller with rs274/ngc (g-code) support + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 +#include +#include "motion_control.h" +#include "gcode.h" +#include "spindle_control.h" +#include "serial_protocol.h" + +int main(void) +{ + mc_init(); // initialize motion control subsystem + gc_init(); // initialize gcode-parser + spindle_init(); // initialize spindle controller + sp_init(); // initialize the serial protocol + + gc_execute_line("123.1"); + for(;;){ + sleep_mode(); + sp_process(); // process the serial protocol + } + return 0; /* never reached */ +} diff --git a/motion_control.c b/motion_control.c new file mode 100644 index 0000000..ad41216 --- /dev/null +++ b/motion_control.c @@ -0,0 +1,338 @@ +/* + motion_control.c - cartesian robot controller. + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 . +*/ + +/* This code was inspired by the Arduino GCode_Interpreter by Mike Ellery. */ + +#include +#include "config.h" +#include "motion_control.h" +#include +#include +#include +#include "nuts_bolts.h" + +// position represents the current position of the head measured in steps +// target is the target for the current linear motion +// step_count contains the absolute values of the steps to travel along each axis +// direction is the sign of the motion for each axis (-1: reverse, 0: standby, 1: forward) + +#define MODE_AT_REST 0 +#define MODE_LINEAR 1 +#define MODE_ARC 2 +#define MODE_DWELL 3 +#define MODE_HOME 4 +#define MODE_LIMIT_OVERRUN -1 + +#define PHASE_HOME_RETURN 0 +#define PHASE_HOME_NUDGE 1 + +#define ONE_MINUTE_OF_MICROSECONDS 60000000 + +// Parameters when mode is MODE_ARC +struct LinearMotionParameters { + int8_t direction[3]; // The direction of travel along each axis (-1, 0 or 1) + uint16_t feed_rate; + int32_t target[3], // The target position in absolute steps + step_count[3], // Absolute steps of travel along each axis + counter[3], // A counter used in the bresenham algorithm for line plotting + maximum_steps; // The larges absolute step-count of any axis +}; + +// Parameters when mode is MODE_LINEAR +struct ArcMotionParameters { + uint32_t radius; + int16_t degrees; + int ccw; +}; + +struct HomeCycleParameters { + int8_t direction[3]; // The direction of travel along each axis (-1, 0 or 1) + int8_t phase; // current phase of the home cycle. + int8_t away[3]; // a vector of booleans. True for each axis that is still away. +}; + +/* The whole state of the motion-control-system in one struct. Makes the code a little bit hard to + read, but lets us initialize the state of the system by just clearing a single, contigous block of memory. + By overlaying the variables of the different modes in a union we save a few bytes of precious SRAM. +*/ +struct MotionControlState { + int8_t mode; // The current operation mode + int32_t position[3]; // The current position of the tool in absolute steps + int32_t update_delay_us; // Microseconds between each update in the current mode + union { + struct LinearMotionParameters linear; // variables used in MODE_LINEAR + struct ArcMotionParameters arc; // variables used in MODE_ARC + struct HomeCycleParameters home; // variables used in MODE_HOME + uint32_t dwell_milliseconds; // variable used in MODE_DWELL + int8_t limit_overrun_direction[3]; // variable used in MODE_LIMIT_OVERRUN + }; +}; +struct MotionControlState state; + +int check_limit_switches(); +void enable_steppers(); +void disable_steppers(); +void set_direction_pins(int8_t *direction); +inline void step_steppers(uint8_t *enabled); +void limit_overrun(uint8_t *direction); +int check_limit_switch(int axis); +inline void step_axis(uint8_t axis); + +void mc_init() +{ + // Initialize state variables + memset(&state, 0, sizeof(state)); + + // Configure directions of interface pins + STEP_DDR |= STEP_MASK; + DIRECTION_DDR |= DIRECTION_MASK; + LIMIT_DDR &= ~(LIMIT_MASK); + STEPPERS_ENABLE_DDR |= 1< 0) + { + step[axis] = true; + state.linear.counter[axis] -= state.linear.maximum_steps; + state.position[axis] += state.linear.direction[axis]; + } + } + } + + if (step[X_AXIS] | step[Y_AXIS] | step[Z_AXIS]) { + step_steppers(step); + + // If we trip any limit switch while moving: Abort, abort! + if (check_limit_switches()) { + limit_overrun(state.linear.direction); + } + + _delay_us(state.update_delay_us); + } else { + state.mode = MODE_AT_REST; + } +} + +void mc_go_home() +{ + state.mode = MODE_HOME; + memset(state.home.direction, -1, sizeof(state.home.direction)); // direction = [-1,-1,-1] + set_direction_pins(state.home.direction); + clear_vector(state.home.away); +} + +void perform_go_home() +{ + int axis; + if(state.home.phase == PHASE_HOME_RETURN) { + // We are running all axes in reverse until all limit switches are tripped + // Check all limit switches: + for(axis=X_AXIS; axis <= Z_AXIS; axis++) { + state.home.away[axis] |= check_limit_switch(axis); + } + // Step steppers. First retract along Z-axis. Then X and Y. + if(state.home.away[Z_AXIS]) { + step_axis(Z_AXIS); + } else { + // Check if all axes are home + if(!(state.home.away[X_AXIS] || state.home.away[Y_AXIS])) { + // All axes are home, prepare next phase: to nudge the tool carefully out of the limit switches + memset(state.home.direction, 1, sizeof(state.home.direction)); // direction = [1,1,1] + set_direction_pins(state.home.direction); + state.home.phase == PHASE_HOME_NUDGE; + return; + } + step_steppers(state.home.away); + } + } else { + for(axis=X_AXIS; axis <= Z_AXIS; axis++) { + if(check_limit_switch(axis)) { + step_axis(axis); + return; + } + } + // When this code is reached it means all axes are free of their limit-switches. Complete the cycle and rest: + clear_vector(state.position); // By definition this is location [0, 0, 0] + state.mode = MODE_AT_REST; + } +} + +void mc_execute() { + enable_steppers(); + while(state.mode) { + switch(state.mode) { + case MODE_AT_REST: break; + case MODE_DWELL: _delay_ms(state.dwell_milliseconds); state.mode = MODE_AT_REST; break; + case MODE_LINEAR: perform_linear_motion(); + case MODE_HOME: perform_go_home(); + } + _delay_us(state.update_delay_us); + } + disable_steppers(); +} + +void mc_wait() { + return; // No concurrency support yet. So waiting for all to pass is moot. +} + +int mc_status() +{ + return(state.mode); +} + +int check_limit_switches() +{ + // Dual read as crude debounce + return((LIMIT_PORT & LIMIT_MASK) | (LIMIT_PORT & LIMIT_MASK)); +} + +int check_limit_switch(int axis) +{ + uint8_t mask = 0; + switch (axis) { + case X_AXIS: mask = 1<>(7-X_DIRECTION_BIT)) | + ((direction[Y_AXIS]&128)>>(7-Y_DIRECTION_BIT)) | + ((direction[Z_AXIS]&128)>>(7-Z_DIRECTION_BIT)) + ); + DIRECTION_PORT = DIRECTION_PORT & ~(DIRECTION_MASK) | forward_bits; +} + +// Step enabled steppers. Enabled should be an array of three bytes. Each byte represent one +// stepper motor in the order X, Y, Z. Set the bytes of the steppers you want to step to +// 1, and the rest to 0. +inline void step_steppers(uint8_t *enabled) +{ + STEP_PORT |= enabled[X_AXIS]<. +*/ + +#ifndef motion_control_h +#define motion_control_h + +#include + +/* All coordinates are in step-counts. */ + +// Initializes the motion_control subsystem resources +void mc_init(); + +// Prepare for linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second +// unless invert_feed_rate is true. Then the feed_rate states the number of seconds for the whole movement. +void mc_linear_motion(double x, double y, double z, float feed_rate, int invert_feed_rate); +// Prepare linear motion relative to the current position. +void mc_dwell(uint32_t milliseconds); +// Prepare to send the tool position home +void mc_go_home(); +// Start the prepared operation. +void mc_execute(); + +// Block until the motion control system is idle +void mc_wait(); + + +// Check motion control status. result == 0: the system is idle. result > 0: the system is busy, +// result < 0: the system is in an error state. +int mc_status(); + +#endif diff --git a/nuts_bolts.h b/nuts_bolts.h new file mode 100644 index 0000000..182fc27 --- /dev/null +++ b/nuts_bolts.h @@ -0,0 +1,41 @@ +/* + motion_control.h - cartesian robot controller. + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 nuts_bolts_h +#define nuts_bolts_h + +#include + +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +#define false 0 +#define true 1 + +// Decide the sign of a value +#define sign(a) (a>0 ? 1 : ((a<0) ? -1 : 0)) + +#define clear_vector(a) memset(a, 0, sizeof(a)) + +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 + +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..468c0b5 --- /dev/null +++ b/readme.txt @@ -0,0 +1,15 @@ +Grbl - An embedded rs274/ngc (g-code) interpreter, cartesian bot controller, readout and exerciser +Inspired by the Arduino GCode Interpreter by Mike Ellery + +Goals: +* Suitable for both milling and deposition fabrication +* Support GCode from common free and cheap CAM-packages right out of the box +* Optional support for a alphanumeric LCD readout, a joystick and a few buttons for program control +* Optional support for automated cutter length calibration when milling +* Support "headless" fabrication by buffering all code to SD-card or similar + +Limitations: +* No support for Arduino software (but will run fine on an Arduino board if you program it with an ISP) +* Limited GCode-support. Focus on the kind of GCode produced by automated CAM tools. Leave human GCoders frustrated. + + diff --git a/serial_protocol.c b/serial_protocol.c new file mode 100644 index 0000000..b704686 --- /dev/null +++ b/serial_protocol.c @@ -0,0 +1,89 @@ +/* + serial_protocol.c - the serial protocol master control unit + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 +#include "serial_protocol.h" +#include "gcode.h" +#include "wiring_serial.h" +#include "config.h" +#include +#include "nuts_bolts.h" + +#define BLOCK_BUFFER_SIZE 128 + +char line[BLOCK_BUFFER_SIZE]; +uint8_t line_counter; + +void prompt() { + printString(PROMPT); +} + +void print_result() { + double position[3]; + int inches_mode; + uint8_t status_code; + uint32_t line_number; + gc_get_status(position, &status_code, &inches_mode, &line_number); + printByte('['); + printInteger(trunc(position[X_AXIS]*100)); + printByte(','); + printInteger(trunc(position[Y_AXIS]*100)); + printByte(','); + printInteger(trunc(position[Z_AXIS]*100)); + printByte(']'); + printByte(' '); + printByte('@'); + printInteger(line_number); + printByte(':'); + switch(status_code) { + case GCSTATUS_OK: printString("0 OK\n"); break; + case GCSTATUS_BAD_NUMBER_FORMAT: printString("1 Bad number format\n"); + case GCSTATUS_EXPECTED_COMMAND_LETTER: printString("2 Expected command letter\n"); break; + case GCSTATUS_UNSUPPORTED_STATEMENT: printString("3 Unsupported statement\n"); break; + case GCSTATUS_MOTION_CONTROL_ERROR: printString("4 Motion control error\n"); break; + } +} + +void sp_init() +{ + beginSerial(BAUD_RATE); + + printString("Grbl "); + printString(VERSION); + printByte('\n'); + prompt(); +} + +void sp_process() +{ + char c; + while((c = serialRead()) != -1) + { + if(c == '\n') { + line[line_counter] = 0; + gc_execute_line(line); + line_counter = 0; + print_result(); + prompt(); + } else { + line[line_counter] = c; + } + } +} diff --git a/serial_protocol.h b/serial_protocol.h new file mode 100644 index 0000000..00d3e0d --- /dev/null +++ b/serial_protocol.h @@ -0,0 +1,28 @@ +/* + serial_protocol.h - the serial protocol master control unit + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 serial_h +#define serial_h + +#define PROMPT ">>>" + +void sp_init(); +void sp_process(); + +#endif diff --git a/spindle_control.c b/spindle_control.c new file mode 100644 index 0000000..4dbf9fd --- /dev/null +++ b/spindle_control.c @@ -0,0 +1,44 @@ +/* + spindle_control.c - spindle control methods + Part of Grbl + + Copyright (c) 2009 Simen Svale Skogsrud + + 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 "spindle_control.h" +#include "config.h" + +#include + +void spindle_init() +{ + SPINDLE_ENABLE_DDR |= 1<= 0) { + SPINDLE_DIRECTION_PORT &= ~(1<. +*/ + +#ifndef spindle_control_h +#define spindle_control_h + +#include + +void spindle_init(); +void spindle_run(int direction, uint32_t rpm); +void spindle_stop(); + +#endif diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..d6ee5dd --- /dev/null +++ b/todo.txt @@ -0,0 +1,7 @@ +* Use timer interrupts to drive steppers +* Tool table +* Tool length offsets +* Tool change M6 +* Coolant control +* Path Control Modes +* Spindle speed support \ No newline at end of file diff --git a/wiring_private.h b/wiring_private.h new file mode 100644 index 0000000..3014075 --- /dev/null +++ b/wiring_private.h @@ -0,0 +1,56 @@ +/* + wiring_private.h - Internal header file. + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2005-2006 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 239 2007-01-12 17:58:39Z mellis $ +*/ + +#ifndef WiringPrivate_h +#define WiringPrivate_h + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +#define EXTERNAL_INT_0 0 +#define EXTERNAL_INT_1 1 + +#define EXTERNAL_NUM_INTERRUPTS 2 + +typedef void (*voidFuncPtr)(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/wiring_serial.c b/wiring_serial.c new file mode 100644 index 0000000..9392a09 --- /dev/null +++ b/wiring_serial.c @@ -0,0 +1,212 @@ +/* + wiring_serial.c - serial functions. + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2005-2006 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $ +*/ + +#include "wiring_private.h" + +// Define constants and variables for buffering incoming serial data. We're +// using a ring buffer (I think), in which rx_buffer_head is the index of the +// location to which to write the next incoming character and rx_buffer_tail +// is the index of the location from which to read. +#define RX_BUFFER_SIZE 128 + +unsigned char rx_buffer[RX_BUFFER_SIZE]; + +int rx_buffer_head = 0; +int rx_buffer_tail = 0; + +void beginSerial(long baud) +{ +#if defined(__AVR_ATmega168__) + UBRR0H = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8; + UBRR0L = ((F_CPU / 16 + baud / 2) / baud - 1); + + // enable rx and tx + sbi(UCSR0B, RXEN0); + sbi(UCSR0B, TXEN0); + + // enable interrupt on complete reception of a byte + sbi(UCSR0B, RXCIE0); +#else + UBRRH = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8; + UBRRL = ((F_CPU / 16 + baud / 2) / baud - 1); + + // enable rx and tx + sbi(UCSRB, RXEN); + sbi(UCSRB, TXEN); + + // enable interrupt on complete reception of a byte + sbi(UCSRB, RXCIE); +#endif + + // defaults to 8-bit, no parity, 1 stop bit +} + +void serialWrite(unsigned char c) +{ +#if defined(__AVR_ATmega168__) + while (!(UCSR0A & (1 << UDRE0))) + ; + + UDR0 = c; +#else + while (!(UCSRA & (1 << UDRE))) + ; + + UDR = c; +#endif +} + +int serialAvailable() +{ + return (RX_BUFFER_SIZE + rx_buffer_head - rx_buffer_tail) % RX_BUFFER_SIZE; +} + +int serialRead() +{ + // if the head isn't ahead of the tail, we don't have any characters + if (rx_buffer_head == rx_buffer_tail) { + return -1; + } else { + unsigned char c = rx_buffer[rx_buffer_tail]; + rx_buffer_tail = (rx_buffer_tail + 1) % RX_BUFFER_SIZE; + return c; + } +} + +void serialFlush() +{ + // don't reverse this or there may be problems if the RX interrupt + // occurs after reading the value of rx_buffer_head but before writing + // the value to rx_buffer_tail; the previous value of rx_buffer_head + // may be written to rx_buffer_tail, making it appear as if the buffer + // were full, not empty. + rx_buffer_head = rx_buffer_tail; +} + +#if defined(__AVR_ATmega168__) +SIGNAL(SIG_USART_RECV) +#else +SIGNAL(SIG_UART_RECV) +#endif +{ +#if defined(__AVR_ATmega168__) + unsigned char c = UDR0; +#else + unsigned char c = UDR; +#endif + + int i = (rx_buffer_head + 1) % RX_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != rx_buffer_tail) { + rx_buffer[rx_buffer_head] = c; + rx_buffer_head = i; + } +} + +void printMode(int mode) +{ + // do nothing, we only support serial printing, not lcd. +} + +void printByte(unsigned char c) +{ + serialWrite(c); +} + +void printNewline() +{ + printByte('\n'); +} + +void printString(const char *s) +{ + while (*s) + printByte(*s++); +} + +void printIntegerInBase(unsigned long n, unsigned long base) +{ + unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. + unsigned long i = 0; + + if (n == 0) { + printByte('0'); + return; + } + + while (n > 0) { + buf[i++] = n % base; + n /= base; + } + + for (; i > 0; i--) + printByte(buf[i - 1] < 10 ? + '0' + buf[i - 1] : + 'A' + buf[i - 1] - 10); +} + +void printInteger(long n) +{ + if (n < 0) { + printByte('-'); + n = -n; + } + + printIntegerInBase(n, 10); +} + +void printHex(unsigned long n) +{ + printIntegerInBase(n, 16); +} + +void printOctal(unsigned long n) +{ + printIntegerInBase(n, 8); +} + +void printBinary(unsigned long n) +{ + printIntegerInBase(n, 2); +} + +/* Including print() adds approximately 1500 bytes to the binary size, + * so we replace it with the smaller and less-confusing printString(), + * printInteger(), etc. +void print(const char *format, ...) +{ + char buf[256]; + va_list ap; + + va_start(ap, format); + vsnprintf(buf, 256, format, ap); + va_end(ap); + + printString(buf); +} +*/ diff --git a/wiring_serial.h b/wiring_serial.h new file mode 100644 index 0000000..2c932fb --- /dev/null +++ b/wiring_serial.h @@ -0,0 +1,43 @@ +/* + Based on wiring.h - Partial implementation of the Wiring API for the ATmega8. + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2005-2006 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 387 2008-03-08 21:30:00Z mellis $ +*/ + +#ifndef wiring_h +#define wiring_h + +void beginSerial(long); +void serialWrite(unsigned char); +int serialAvailable(void); +int serialRead(void); +void serialFlush(void); +void printMode(int); +void printByte(unsigned char c); +void printNewline(void); +void printString(const char *s); +void printInteger(long n); +void printHex(unsigned long n); +void printOctal(unsigned long n); +void printBinary(unsigned long n); +void printIntegerInBase(unsigned long n, unsigned long base); + +#endif