Added runtime configurable global settings with eeprom persitence
This commit is contained in:
parent
a6b8d73044
commit
b8ba8a4231
6
Makefile
6
Makefile
@ -30,8 +30,10 @@
|
|||||||
DEVICE = atmega168
|
DEVICE = atmega168
|
||||||
CLOCK = 16000000
|
CLOCK = 16000000
|
||||||
PROGRAMMER = -c avrisp2 -P usb
|
PROGRAMMER = -c avrisp2 -P usb
|
||||||
OBJECTS = main.o motion_control.o gcode.o spindle_control.o wiring_serial.o serial_protocol.o stepper.o
|
OBJECTS = main.o motion_control.o gcode.o spindle_control.o wiring_serial.o serial_protocol.o stepper.o \
|
||||||
FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
|
eeprom.o config.o
|
||||||
|
# FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
|
||||||
|
FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m
|
||||||
|
|
||||||
# Tune the lines below only if you know what you are doing:
|
# Tune the lines below only if you know what you are doing:
|
||||||
|
|
||||||
|
95
config.c
Normal file
95
config.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
config.c - eeprom and compile time configuration handling
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "nuts_bolts.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "eeprom.h"
|
||||||
|
#include "wiring_serial.h"
|
||||||
|
|
||||||
|
void reset_settings() {
|
||||||
|
settings.steps_per_mm[0] = X_STEPS_PER_MM;
|
||||||
|
settings.steps_per_mm[1] = Y_STEPS_PER_MM;
|
||||||
|
settings.steps_per_mm[2] = Z_STEPS_PER_MM;
|
||||||
|
settings.pulse_microseconds = STEP_PULSE_MICROSECONDS;
|
||||||
|
settings.default_feed_rate = DEFAULT_FEEDRATE;
|
||||||
|
settings.default_seek_rate = RAPID_FEEDRATE;
|
||||||
|
settings.mm_per_arc_segment = MM_PER_ARC_SEGMENT;
|
||||||
|
settings.invert_mask = STEPPING_INVERT_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_settings() {
|
||||||
|
printString("$0 = "); printFloat(settings.steps_per_mm[0]);
|
||||||
|
printString(" (steps/mm x)\r\n$1 = "); printFloat(settings.steps_per_mm[1]);
|
||||||
|
printString(" (steps/mm y)\r\n$2 = "); printFloat(settings.steps_per_mm[2]);
|
||||||
|
printString(" (steps/mm z)\r\n$3 = "); printInteger(settings.pulse_microseconds);
|
||||||
|
printString(" (microseconds step pulse)\r\n$4 = "); printFloat(settings.default_feed_rate);
|
||||||
|
printString(" (mm/sec default feed rate)\r\n$5 = "); printFloat(settings.default_seek_rate);
|
||||||
|
printString(" (mm/sec default seek rate)\r\n$6 = "); printFloat(settings.mm_per_arc_segment);
|
||||||
|
printString(" (mm/arc segment)\r\n$7 = "); printInteger(settings.invert_mask);
|
||||||
|
printString(" (step port invert mask. binary = "); printIntegerInBase(settings.invert_mask, 2);
|
||||||
|
printString(")\r\n\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_settings() {
|
||||||
|
// Check version-byte of eeprom
|
||||||
|
uint8_t version = eeprom_get_char(0);
|
||||||
|
if (version != SETTINGS_VERSION) { return(FALSE); }
|
||||||
|
// Read settings-record and check checksum
|
||||||
|
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(struct Settings)))) {
|
||||||
|
return(FALSE);
|
||||||
|
}
|
||||||
|
return(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_settings() {
|
||||||
|
eeprom_put_char(0, SETTINGS_VERSION);
|
||||||
|
memcpy_to_eeprom_with_checksum(1, (char*)&settings, sizeof(struct Settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper method to set settings from command line
|
||||||
|
void store_setting(int parameter, double value) {
|
||||||
|
switch(parameter) {
|
||||||
|
case 0: case 1: case 2:
|
||||||
|
settings.steps_per_mm[parameter] = value; break;
|
||||||
|
case 3: settings.pulse_microseconds = round(value); break;
|
||||||
|
case 4: settings.default_feed_rate = value; break;
|
||||||
|
case 5: settings.default_seek_rate = value; break;
|
||||||
|
case 6: settings.mm_per_arc_segment = value; break;
|
||||||
|
case 7: settings.invert_mask = trunc(value); break;
|
||||||
|
default:
|
||||||
|
printString("Unknown parameter\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
write_settings();
|
||||||
|
printString("Stored new setting\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_init() {
|
||||||
|
if(read_settings()) {
|
||||||
|
printString("'$' to dump current settings\r\n");
|
||||||
|
} else {
|
||||||
|
printString("EEPROM blank. Rewrote default settings:\r\n");
|
||||||
|
reset_settings();
|
||||||
|
write_settings();
|
||||||
|
dump_settings();
|
||||||
|
}
|
||||||
|
}
|
70
config.h
70
config.h
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
config.h - configuration data for Grbl
|
config.h - eeprom and compile time configuration handling
|
||||||
Part of Grbl
|
Part of Grbl
|
||||||
|
|
||||||
Copyright (c) 2009 Simen Svale Skogsrud
|
Copyright (c) 2009 Simen Svale Skogsrud
|
||||||
@ -21,22 +21,11 @@
|
|||||||
#ifndef config_h
|
#ifndef config_h
|
||||||
#define config_h
|
#define config_h
|
||||||
|
|
||||||
#define VERSION "0.5"
|
#define VERSION "0.51"
|
||||||
|
|
||||||
#define MICROSTEPS 8
|
// Settings that can only be set at compile-time:
|
||||||
#define X_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
|
||||||
#define Y_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
|
||||||
#define Z_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
|
||||||
|
|
||||||
#define STEP_PULSE_MICROSECONDS 30
|
#define BAUD_RATE 9600
|
||||||
|
|
||||||
#define INCHES_PER_MM (1.0/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 480.0 // in millimeters per minute
|
|
||||||
#define DEFAULT_FEEDRATE 480.0
|
|
||||||
|
|
||||||
#define STEPPERS_ENABLE_DDR DDRD
|
#define STEPPERS_ENABLE_DDR DDRD
|
||||||
#define STEPPERS_ENABLE_PORT PORTD
|
#define STEPPERS_ENABLE_PORT PORTD
|
||||||
@ -65,14 +54,44 @@
|
|||||||
#define SPINDLE_DIRECTION_PORT PORTD
|
#define SPINDLE_DIRECTION_PORT PORTD
|
||||||
#define SPINDLE_DIRECTION_BIT 7
|
#define SPINDLE_DIRECTION_BIT 7
|
||||||
|
|
||||||
|
|
||||||
|
// 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 1
|
||||||
|
|
||||||
|
// Current global settings (persisted in EEPROM from byte 1 onwards)
|
||||||
|
struct Settings {
|
||||||
|
double steps_per_mm[3];
|
||||||
|
uint8_t microsteps;
|
||||||
|
uint8_t pulse_microseconds;
|
||||||
|
double default_feed_rate;
|
||||||
|
double default_seek_rate;
|
||||||
|
uint8_t invert_mask;
|
||||||
|
double mm_per_arc_segment;
|
||||||
|
};
|
||||||
|
struct Settings settings;
|
||||||
|
|
||||||
|
// Initialize the configuration subsystem (load settings from EEPROM)
|
||||||
|
void config_init();
|
||||||
|
|
||||||
|
// Print current settings
|
||||||
|
void dump_settings();
|
||||||
|
|
||||||
|
// A helper method to set new settings from command line
|
||||||
|
void store_setting(int parameter, double value);
|
||||||
|
|
||||||
|
|
||||||
|
// Default settings (used when resetting eeprom-settings)
|
||||||
|
#define MICROSTEPS 8
|
||||||
|
#define X_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||||
|
#define Y_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||||
|
#define Z_STEPS_PER_MM (94.488188976378*MICROSTEPS)
|
||||||
|
#define STEP_PULSE_MICROSECONDS 30
|
||||||
|
|
||||||
#define MM_PER_ARC_SEGMENT 0.1
|
#define MM_PER_ARC_SEGMENT 0.1
|
||||||
|
|
||||||
#define BAUD_RATE 9600
|
#define RAPID_FEEDRATE 480.0 // in millimeters per minute
|
||||||
|
#define DEFAULT_FEEDRATE 480.0
|
||||||
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT))
|
|
||||||
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
|
|
||||||
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK)
|
|
||||||
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT))
|
|
||||||
|
|
||||||
// Use this line for default operation (step-pulses high)
|
// Use this line for default operation (step-pulses high)
|
||||||
#define STEPPING_INVERT_MASK 0
|
#define STEPPING_INVERT_MASK 0
|
||||||
@ -83,4 +102,13 @@
|
|||||||
// Or bake your own like this adding any step-bits or directions you want to invert:
|
// Or bake your own like this adding any step-bits or directions you want to invert:
|
||||||
// #define STEPPING_INVERT_MASK (STEP_MASK | (1<<X_DIRECTION_BIT) | (1<<Y_DIRECTION_BIT))
|
// #define STEPPING_INVERT_MASK (STEP_MASK | (1<<X_DIRECTION_BIT) | (1<<Y_DIRECTION_BIT))
|
||||||
|
|
||||||
|
|
||||||
|
// Some useful constants
|
||||||
|
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
|
||||||
|
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
|
||||||
|
#define STEPPING_MASK (STEP_MASK | DIRECTION_MASK) // All stepping-related bits (step/direction)
|
||||||
|
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
|
||||||
|
|
||||||
|
#define INCHES_PER_MM (1.0/25.4) // A conversion rate
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
148
eeprom.c
Executable file
148
eeprom.c
Executable file
@ -0,0 +1,148 @@
|
|||||||
|
// This file has been prepared for Doxygen automatic documentation generation.
|
||||||
|
/*! \file ********************************************************************
|
||||||
|
*
|
||||||
|
* Atmel Corporation
|
||||||
|
*
|
||||||
|
* \li File: eeprom.c
|
||||||
|
* \li Compiler: IAR EWAAVR 3.10c
|
||||||
|
* \li Support mail: avr@atmel.com
|
||||||
|
*
|
||||||
|
* \li Supported devices: All devices with split EEPROM erase/write
|
||||||
|
* capabilities can be used.
|
||||||
|
* The example is written for ATmega48.
|
||||||
|
*
|
||||||
|
* \li AppNote: AVR103 - Using the EEPROM Programming Modes.
|
||||||
|
*
|
||||||
|
* \li Description: Example on how to use the split EEPROM erase/write
|
||||||
|
* capabilities in e.g. ATmega48. All EEPROM
|
||||||
|
* programming modes are tested, i.e. Erase+Write,
|
||||||
|
* Erase-only and Write-only.
|
||||||
|
*
|
||||||
|
* $Revision: 1.6 $
|
||||||
|
* $Date: Friday, February 11, 2005 07:16:44 UTC $
|
||||||
|
****************************************************************************/
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
/* These EEPROM bits have different names on different devices. */
|
||||||
|
#ifndef EEPE
|
||||||
|
#define EEPE EEWE //!< EEPROM program/write enable.
|
||||||
|
#define EEMPE EEMWE //!< EEPROM master program/write enable.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* These two are unfortunately not defined in the device include files. */
|
||||||
|
#define EEPM1 5 //!< EEPROM Programming Mode Bit 1.
|
||||||
|
#define EEPM0 4 //!< EEPROM Programming Mode Bit 0.
|
||||||
|
|
||||||
|
/* Define to reduce code size. */
|
||||||
|
#define EEPROM_IGNORE_SELFPROG //!< Remove SPM flag polling.
|
||||||
|
|
||||||
|
/*! \brief Read byte from EEPROM.
|
||||||
|
*
|
||||||
|
* This function reads one byte from a given EEPROM address.
|
||||||
|
*
|
||||||
|
* \note The CPU is halted for 4 clock cycles during EEPROM read.
|
||||||
|
*
|
||||||
|
* \param addr EEPROM address to read from.
|
||||||
|
* \return The byte read from the EEPROM address.
|
||||||
|
*/
|
||||||
|
unsigned char eeprom_get_char( unsigned int addr )
|
||||||
|
{
|
||||||
|
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
|
||||||
|
EEAR = addr; // Set EEPROM address register.
|
||||||
|
EECR = (1<<EERE); // Start EEPROM read operation.
|
||||||
|
return EEDR; // Return the byte read from EEPROM.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Write byte to EEPROM.
|
||||||
|
*
|
||||||
|
* This function writes one byte to a given EEPROM address.
|
||||||
|
* The differences between the existing byte and the new value is used
|
||||||
|
* to select the most efficient EEPROM programming mode.
|
||||||
|
*
|
||||||
|
* \note The CPU is halted for 2 clock cycles during EEPROM programming.
|
||||||
|
*
|
||||||
|
* \note When this function returns, the new EEPROM value is not available
|
||||||
|
* until the EEPROM programming time has passed. The EEPE bit in EECR
|
||||||
|
* should be polled to check whether the programming is finished.
|
||||||
|
*
|
||||||
|
* \note The EEPROM_GetChar() function checks the EEPE bit automatically.
|
||||||
|
*
|
||||||
|
* \param addr EEPROM address to write to.
|
||||||
|
* \param new_value New EEPROM value.
|
||||||
|
*/
|
||||||
|
void eeprom_put_char( unsigned int addr, unsigned char new_value )
|
||||||
|
{
|
||||||
|
char old_value; // Old EEPROM value.
|
||||||
|
char diff_mask; // Difference mask, i.e. old value XOR new value.
|
||||||
|
|
||||||
|
cli(); // Ensure atomic operation for the write operation.
|
||||||
|
|
||||||
|
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
|
||||||
|
#ifndef EEPROM_IGNORE_SELFPROG
|
||||||
|
do {} while( SPMCSR & (1<<SELFPRGEN) ); // Wait for completion of SPM.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EEAR = addr; // Set EEPROM address register.
|
||||||
|
EECR = (1<<EERE); // Start EEPROM read operation.
|
||||||
|
old_value = EEDR; // Get old EEPROM value.
|
||||||
|
diff_mask = old_value ^ new_value; // Get bit differences.
|
||||||
|
|
||||||
|
// Check if any bits are changed to '1' in the new value.
|
||||||
|
if( diff_mask & new_value ) {
|
||||||
|
// Now we know that _some_ bits need to be erased to '1'.
|
||||||
|
|
||||||
|
// Check if any bits in the new value are '0'.
|
||||||
|
if( new_value != 0xff ) {
|
||||||
|
// Now we know that some bits need to be programmed to '0' also.
|
||||||
|
|
||||||
|
EEDR = new_value; // Set EEPROM data register.
|
||||||
|
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||||
|
(0<<EEPM1) | (0<<EEPM0); // ...and Erase+Write mode.
|
||||||
|
EECR |= (1<<EEPE); // Start Erase+Write operation.
|
||||||
|
} else {
|
||||||
|
// Now we know that all bits should be erased.
|
||||||
|
|
||||||
|
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||||
|
(1<<EEPM0); // ...and Erase-only mode.
|
||||||
|
EECR |= (1<<EEPE); // Start Erase-only operation.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Now we know that _no_ bits need to be erased to '1'.
|
||||||
|
|
||||||
|
// Check if any bits are changed from '1' in the old value.
|
||||||
|
if( diff_mask ) {
|
||||||
|
// Now we know that _some_ bits need to the programmed to '0'.
|
||||||
|
|
||||||
|
EEDR = new_value; // Set EEPROM data register.
|
||||||
|
EECR = (1<<EEMPE) | // Set Master Write Enable bit...
|
||||||
|
(1<<EEPM1); // ...and Write-only mode.
|
||||||
|
EECR |= (1<<EEPE); // Start Write-only operation.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sei(); // Restore interrupt flag state.
|
||||||
|
}
|
||||||
|
|
||||||
|
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
|
||||||
|
unsigned char checksum = 0;
|
||||||
|
for(; size > 0; size--) {
|
||||||
|
checksum = (checksum << 1) || (checksum >> 7);
|
||||||
|
checksum += *source;
|
||||||
|
eeprom_put_char(destination++, *(source++));
|
||||||
|
}
|
||||||
|
eeprom_put_char(destination, checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) {
|
||||||
|
unsigned char data, checksum = 0;
|
||||||
|
for(; size > 0; size--) {
|
||||||
|
data = eeprom_get_char(source++);
|
||||||
|
checksum = (checksum << 1) || (checksum >> 7);
|
||||||
|
checksum += data;
|
||||||
|
*(destination++) = data;
|
||||||
|
}
|
||||||
|
return(checksum == eeprom_get_char(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of file
|
9
eeprom.h
Normal file
9
eeprom.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef eeprom_h
|
||||||
|
#define eeprom_h
|
||||||
|
|
||||||
|
char eeprom_get_char(unsigned int addr);
|
||||||
|
void eeprom_put_char(unsigned int addr, char new_value);
|
||||||
|
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size);
|
||||||
|
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size);
|
||||||
|
|
||||||
|
#endif
|
18
gcode.c
18
gcode.c
@ -83,7 +83,7 @@ struct ParserState {
|
|||||||
uint8_t absolute_mode; /* 0 = relative motion, 1 = absolute motion {G90, G91} */
|
uint8_t absolute_mode; /* 0 = relative motion, 1 = absolute motion {G90, G91} */
|
||||||
uint8_t program_flow;
|
uint8_t program_flow;
|
||||||
int spindle_direction;
|
int spindle_direction;
|
||||||
double feed_rate; /* Millimeters/second */
|
double feed_rate, seek_rate; /* Millimeters/second */
|
||||||
double position[3]; /* Where the interpreter considers the tool to be at this point in the code */
|
double position[3]; /* Where the interpreter considers the tool to be at this point in the code */
|
||||||
uint8_t tool;
|
uint8_t tool;
|
||||||
int16_t spindle_speed; /* RPM/100 */
|
int16_t spindle_speed; /* RPM/100 */
|
||||||
@ -110,7 +110,8 @@ void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
|
|||||||
|
|
||||||
void gc_init() {
|
void gc_init() {
|
||||||
memset(&gc, 0, sizeof(gc));
|
memset(&gc, 0, sizeof(gc));
|
||||||
gc.feed_rate = DEFAULT_FEEDRATE/60;
|
gc.feed_rate = settings.default_feed_rate/60;
|
||||||
|
gc.seek_rate = settings.default_seek_rate/60;
|
||||||
select_plane(X_AXIS, Y_AXIS, Z_AXIS);
|
select_plane(X_AXIS, Y_AXIS, Z_AXIS);
|
||||||
gc.absolute_mode = TRUE;
|
gc.absolute_mode = TRUE;
|
||||||
}
|
}
|
||||||
@ -163,6 +164,16 @@ uint8_t gc_execute_line(char *line) {
|
|||||||
|
|
||||||
if (line[0] == '(') { return(gc.status_code); }
|
if (line[0] == '(') { return(gc.status_code); }
|
||||||
if (line[0] == '/') { counter++; } // ignore block delete
|
if (line[0] == '/') { counter++; } // ignore block delete
|
||||||
|
if (line[0] == '$') { // This is a parameter line intended to change EEPROM-settings
|
||||||
|
// Parameter lines are on the form '$4=374.3' or '$' to dump current settings
|
||||||
|
counter = 1;
|
||||||
|
if(line[counter] == 0) { dump_settings(); return(GCSTATUS_OK); }
|
||||||
|
read_double(line, &counter, &p);
|
||||||
|
if(line[counter++] != '=') { return(GCSTATUS_UNSUPPORTED_STATEMENT); }
|
||||||
|
read_double(line, &counter, &value);
|
||||||
|
if(line[counter] != 0) { return(GCSTATUS_UNSUPPORTED_STATEMENT); }
|
||||||
|
store_setting(p, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Pass 1: Commands
|
// Pass 1: Commands
|
||||||
while(next_statement(&letter, &value, line, &counter)) {
|
while(next_statement(&letter, &value, line, &counter)) {
|
||||||
@ -256,7 +267,8 @@ uint8_t gc_execute_line(char *line) {
|
|||||||
case NEXT_ACTION_DEFAULT:
|
case NEXT_ACTION_DEFAULT:
|
||||||
switch (gc.motion_mode) {
|
switch (gc.motion_mode) {
|
||||||
case MOTION_MODE_CANCEL: break;
|
case MOTION_MODE_CANCEL: break;
|
||||||
case MOTION_MODE_RAPID_LINEAR: case MOTION_MODE_LINEAR:
|
case MOTION_MODE_RAPID_LINEAR:
|
||||||
|
case MOTION_MODE_LINEAR:
|
||||||
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS],
|
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS],
|
||||||
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode);
|
(gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode);
|
||||||
break;
|
break;
|
||||||
|
2
main.c
2
main.c
@ -33,12 +33,12 @@
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
beginSerial(BAUD_RATE);
|
beginSerial(BAUD_RATE);
|
||||||
|
config_init();
|
||||||
st_init(); // initialize the stepper subsystem
|
st_init(); // initialize the stepper subsystem
|
||||||
mc_init(); // initialize motion control subsystem
|
mc_init(); // initialize motion control subsystem
|
||||||
spindle_init(); // initialize spindle controller
|
spindle_init(); // initialize spindle controller
|
||||||
gc_init(); // initialize gcode-parser
|
gc_init(); // initialize gcode-parser
|
||||||
sp_init(); // initialize the serial protocol
|
sp_init(); // initialize the serial protocol
|
||||||
// sd_raw_init());
|
|
||||||
|
|
||||||
DDRD |= (1<<3)|(1<<4)|(1<<5);
|
DDRD |= (1<<3)|(1<<4)|(1<<5);
|
||||||
|
|
||||||
|
@ -51,9 +51,9 @@ void mc_line(double x, double y, double z, float feed_rate, int invert_feed_rate
|
|||||||
int32_t target[3]; // The target position in absolute steps
|
int32_t target[3]; // The target position in absolute steps
|
||||||
int32_t steps[3]; // The target line in relative steps
|
int32_t steps[3]; // The target line in relative steps
|
||||||
|
|
||||||
target[X_AXIS] = lround(x*X_STEPS_PER_MM);
|
target[X_AXIS] = lround(x*settings.steps_per_mm[0]);
|
||||||
target[Y_AXIS] = lround(y*Y_STEPS_PER_MM);
|
target[Y_AXIS] = lround(y*settings.steps_per_mm[1]);
|
||||||
target[Z_AXIS] = lround(z*Z_STEPS_PER_MM);
|
target[Z_AXIS] = lround(z*settings.steps_per_mm[2]);
|
||||||
|
|
||||||
for(axis = X_AXIS; axis <= Z_AXIS; axis++) {
|
for(axis = X_AXIS; axis <= Z_AXIS; axis++) {
|
||||||
steps[axis] = target[axis]-position[axis];
|
steps[axis] = target[axis]-position[axis];
|
||||||
@ -64,9 +64,9 @@ void mc_line(double x, double y, double z, float feed_rate, int invert_feed_rate
|
|||||||
} else {
|
} else {
|
||||||
// Ask old Phytagoras to estimate how many mm our next move is going to take us
|
// Ask old Phytagoras to estimate how many mm our next move is going to take us
|
||||||
double millimeters_of_travel = sqrt(
|
double millimeters_of_travel = sqrt(
|
||||||
square(steps[X_AXIS]/X_STEPS_PER_MM) +
|
square(steps[X_AXIS]/settings.steps_per_mm[0]) +
|
||||||
square(steps[Y_AXIS]/Y_STEPS_PER_MM) +
|
square(steps[Y_AXIS]/settings.steps_per_mm[1]) +
|
||||||
square(steps[Z_AXIS]/Z_STEPS_PER_MM));
|
square(steps[Z_AXIS]/settings.steps_per_mm[2]));
|
||||||
st_buffer_line(steps[X_AXIS], steps[Y_AXIS], steps[Z_AXIS],
|
st_buffer_line(steps[X_AXIS], steps[Y_AXIS], steps[Z_AXIS],
|
||||||
lround((millimeters_of_travel/feed_rate)*1000000));
|
lround((millimeters_of_travel/feed_rate)*1000000));
|
||||||
}
|
}
|
||||||
@ -80,14 +80,12 @@ void mc_line(double x, double y, double z, float feed_rate, int invert_feed_rate
|
|||||||
|
|
||||||
// The arc is approximated by generating a huge number of tiny, linear segments. The length of each
|
// The arc is approximated by generating a huge number of tiny, linear segments. The length of each
|
||||||
// segment is configured in config.h by setting MM_PER_ARC_SEGMENT.
|
// segment is configured in config.h by setting MM_PER_ARC_SEGMENT.
|
||||||
|
|
||||||
// ISSUE: The arc interpolator assumes all axes have the same steps/mm as the X axis.
|
|
||||||
void mc_arc(double theta, double angular_travel, double radius, double linear_travel, int axis_1, int axis_2,
|
void mc_arc(double theta, double angular_travel, double radius, double linear_travel, int axis_1, int axis_2,
|
||||||
int axis_linear, double feed_rate, int invert_feed_rate)
|
int axis_linear, double feed_rate, int invert_feed_rate)
|
||||||
{
|
{
|
||||||
double millimeters_of_travel = hypot(angular_travel*radius, labs(linear_travel));
|
double millimeters_of_travel = hypot(angular_travel*radius, labs(linear_travel));
|
||||||
if (millimeters_of_travel == 0.0) { return; }
|
if (millimeters_of_travel == 0.0) { return; }
|
||||||
uint16_t segments = ceil(millimeters_of_travel/MM_PER_ARC_SEGMENT);
|
uint16_t segments = ceil(millimeters_of_travel/settings.mm_per_arc_segment);
|
||||||
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
|
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
|
||||||
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
|
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
|
||||||
// all segments.
|
// all segments.
|
||||||
@ -97,8 +95,8 @@ void mc_arc(double theta, double angular_travel, double radius, double linear_tr
|
|||||||
// The linear motion for each segment
|
// The linear motion for each segment
|
||||||
double linear_per_segment = linear_travel/segments;
|
double linear_per_segment = linear_travel/segments;
|
||||||
// Compute the center of this circle
|
// Compute the center of this circle
|
||||||
double center_x = (position[axis_1]/X_STEPS_PER_MM)-sin(theta)*radius;
|
double center_x = (position[axis_1]/settings.steps_per_mm[axis_1])-sin(theta)*radius;
|
||||||
double center_y = (position[axis_2]/Y_STEPS_PER_MM)-cos(theta)*radius;
|
double center_y = (position[axis_2]/settings.steps_per_mm[axis_2])-cos(theta)*radius;
|
||||||
// a vector to track the end point of each segment
|
// a vector to track the end point of each segment
|
||||||
double target[3];
|
double target[3];
|
||||||
int i;
|
int i;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Grbl - An embedded rs274/ngc (g-code) integrater interpreter and motion-controller for the Arduino/AVR328 microcontroller
|
Grbl - An embedded rs274/ngc (g-code) interpreter and motion-controller for the Arduino/AVR328 microcontroller
|
||||||
|
|
||||||
Goal: A no-compromise, high performance, low cost alternative to parallel-port based motion control for CNC milling
|
Goal: A no-compromise, high performance, low cost alternative to parallel-port based motion control for CNC milling
|
||||||
|
|
||||||
@ -10,12 +10,12 @@ Status:
|
|||||||
* Standards-compliant g-code arcs/circles fully supported
|
* Standards-compliant g-code arcs/circles fully supported
|
||||||
* Buffered, non blocking, asynchronous step generation so the rest of the system is free to process
|
* Buffered, non blocking, asynchronous step generation so the rest of the system is free to process
|
||||||
g-code while the steppers are steppin'
|
g-code while the steppers are steppin'
|
||||||
|
* Configuration parameters stored in EEPROM and set via simple commands
|
||||||
* Tested on very few (two) CNC rigs
|
* Tested on very few (two) CNC rigs
|
||||||
|
|
||||||
Pending:
|
Pending:
|
||||||
* Battle hardening in the field
|
* Battle hardening in the field
|
||||||
* Documentation and web-site
|
* Documentation and web-site
|
||||||
* Simpler configuration (w/o recompilation)
|
|
||||||
* Optional support for a alphanumeric LCD readout, a joystick and a few buttons for program control
|
* Optional support for a alphanumeric LCD readout, a joystick and a few buttons for program control
|
||||||
* Support "headless" fabrication by buffering all code to SD-card or similar
|
* Support "headless" fabrication by buffering all code to SD-card or similar
|
||||||
* Easing of feed rate
|
* Easing of feed rate
|
||||||
|
12
stepper.c
12
stepper.c
@ -95,8 +95,8 @@ SIGNAL(SIG_OUTPUT_COMPARE1A)
|
|||||||
// Then pulse the stepping pins
|
// Then pulse the stepping pins
|
||||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
|
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
|
||||||
// Reset step pulse reset timer so that SIG_OVERFLOW2 can reset the signal after
|
// Reset step pulse reset timer so that SIG_OVERFLOW2 can reset the signal after
|
||||||
// exactly STEP_PULSE_MICROSECONDS microseconds.
|
// exactly settings.pulse_microseconds microseconds.
|
||||||
TCNT2 = -(((STEP_PULSE_MICROSECONDS-2)*TICKS_PER_MICROSECOND)/8);
|
TCNT2 = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND)/8);
|
||||||
|
|
||||||
busy = TRUE;
|
busy = TRUE;
|
||||||
sei(); // Re enable interrupts (normally disabled while inside an interrupt handler)
|
sei(); // Re enable interrupts (normally disabled while inside an interrupt handler)
|
||||||
@ -150,17 +150,17 @@ SIGNAL(SIG_OUTPUT_COMPARE1A)
|
|||||||
} else {
|
} else {
|
||||||
out_bits = 0;
|
out_bits = 0;
|
||||||
}
|
}
|
||||||
out_bits ^= STEPPING_INVERT_MASK;
|
out_bits ^= settings.invert_mask;
|
||||||
busy=FALSE;
|
busy=FALSE;
|
||||||
PORTD &= ~(1<<3);
|
PORTD &= ~(1<<3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This interrupt is set up by SIG_OUTPUT_COMPARE1A when it sets the motor port bits. It resets
|
// This interrupt is set up by SIG_OUTPUT_COMPARE1A when it sets the motor port bits. It resets
|
||||||
// the motor port after a short period (STEP_PULSE_MICROSECONDS) completing one step cycle.
|
// the motor port after a short period (settings.pulse_microseconds) completing one step cycle.
|
||||||
SIGNAL(SIG_OVERFLOW2)
|
SIGNAL(SIG_OVERFLOW2)
|
||||||
{
|
{
|
||||||
// reset stepping pins (leave the direction pins)
|
// reset stepping pins (leave the direction pins)
|
||||||
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (STEPPING_INVERT_MASK & STEP_MASK);
|
STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize and start the stepper motor subsystem
|
// Initialize and start the stepper motor subsystem
|
||||||
@ -168,7 +168,7 @@ void st_init()
|
|||||||
{
|
{
|
||||||
// Configure directions of interface pins
|
// Configure directions of interface pins
|
||||||
STEPPING_DDR |= STEPPING_MASK;
|
STEPPING_DDR |= STEPPING_MASK;
|
||||||
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK); //| STEPPING_INVERT_MASK;
|
STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
|
||||||
LIMIT_DDR &= ~(LIMIT_MASK);
|
LIMIT_DDR &= ~(LIMIT_MASK);
|
||||||
STEPPERS_ENABLE_DDR |= 1<<STEPPERS_ENABLE_BIT;
|
STEPPERS_ENABLE_DDR |= 1<<STEPPERS_ENABLE_BIT;
|
||||||
|
|
||||||
|
2
todo.txt
2
todo.txt
@ -1,4 +1,4 @@
|
|||||||
* Use errno to detect fp-errors
|
* Complete support for using and setting separate seek-rate for G0-commnads
|
||||||
* Implement homing cycle in stepper.c
|
* Implement homing cycle in stepper.c
|
||||||
* Implement limit switch support in stepper.c (use port-triggered interrupts?)
|
* Implement limit switch support in stepper.c (use port-triggered interrupts?)
|
||||||
* Tool change M6
|
* Tool change M6
|
||||||
|
@ -180,6 +180,15 @@ void printInteger(long n)
|
|||||||
printIntegerInBase(n, 10);
|
printIntegerInBase(n, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printFloat(double n)
|
||||||
|
{
|
||||||
|
double integer_part, fractional_part;
|
||||||
|
fractional_part = modf(n, &integer_part);
|
||||||
|
printInteger(integer_part);
|
||||||
|
printByte('.');
|
||||||
|
printInteger(round(fractional_part*1000));
|
||||||
|
}
|
||||||
|
|
||||||
// void printHex(unsigned long n)
|
// void printHex(unsigned long n)
|
||||||
// {
|
// {
|
||||||
// printIntegerInBase(n, 16);
|
// printIntegerInBase(n, 16);
|
||||||
|
@ -39,5 +39,6 @@ void printHex(unsigned long n);
|
|||||||
void printOctal(unsigned long n);
|
void printOctal(unsigned long n);
|
||||||
void printBinary(unsigned long n);
|
void printBinary(unsigned long n);
|
||||||
void printIntegerInBase(unsigned long n, unsigned long base);
|
void printIntegerInBase(unsigned long n, unsigned long base);
|
||||||
|
void printFloat(double n);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user