Permalink
Browse files

Added runtime configurable global settings with eeprom persitence

  • Loading branch information...
1 parent a6b8d73 commit b8ba8a4231676eb1bec9b73dd5862f18188c2977 Simen Svale Skogsrud committed Mar 7, 2010
Showing with 350 additions and 48 deletions.
  1. +5 −3 Makefile
  2. +95 −0 config.c
  3. +49 −21 config.h
  4. +148 −0 eeprom.c
  5. +9 −0 eeprom.h
  6. +15 −3 gcode.c
  7. +1 −1 main.c
  8. +9 −11 motion_control.c
  9. +2 −2 readme.txt
  10. +6 −6 stepper.c
  11. +1 −1 todo.txt
  12. +9 −0 wiring_serial.c
  13. +1 −0 wiring_serial.h
View
@@ -30,12 +30,14 @@
DEVICE = atmega168
CLOCK = 16000000
PROGRAMMER = -c avrisp2 -P usb
-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
+OBJECTS = main.o motion_control.o gcode.o spindle_control.o wiring_serial.o serial_protocol.o stepper.o \
+ 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:
-AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F
+AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I.
# symbolic targets:
View
@@ -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();
+ }
+}
View
@@ -1,5 +1,5 @@
/*
- config.h - configuration data for Grbl
+ config.h - eeprom and compile time configuration handling
Part of Grbl
Copyright (c) 2009 Simen Svale Skogsrud
@@ -21,22 +21,11 @@
#ifndef config_h
#define config_h
-#define VERSION "0.5"
+#define VERSION "0.51"
-#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)
+// Settings that can only be set at compile-time:
-#define STEP_PULSE_MICROSECONDS 30
-
-#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 BAUD_RATE 9600
#define STEPPERS_ENABLE_DDR DDRD
#define STEPPERS_ENABLE_PORT PORTD
@@ -65,14 +54,44 @@
#define SPINDLE_DIRECTION_PORT PORTD
#define SPINDLE_DIRECTION_BIT 7
-#define MM_PER_ARC_SEGMENT 0.1
-#define BAUD_RATE 9600
+// 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();
-#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))
+// 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 RAPID_FEEDRATE 480.0 // in millimeters per minute
+#define DEFAULT_FEEDRATE 480.0
// Use this line for default operation (step-pulses high)
#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:
// #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
View
148 eeprom.c
@@ -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
View
@@ -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
Oops, something went wrong.

0 comments on commit b8ba8a4

Please sign in to comment.