From 2b5d6a98716605a8b388d8e98f3fff65be420458 Mon Sep 17 00:00:00 2001 From: Todd Fleming Date: Sat, 14 Jan 2017 23:54:36 -0500 Subject: [PATCH] Flash memory --- grbl-lpc/flash.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++ grbl/config.h | 4 ++ grbl/eeprom.c | 4 ++ grbl/eeprom.h | 2 + grbl/gcode.c | 6 +-- grbl/main.c | 1 + grbl/settings.c | 33 +++++++++++----- grbl/settings.h | 2 +- lpc17xx/lpc1769.ld | 26 ++++++++----- 9 files changed, 148 insertions(+), 24 deletions(-) create mode 100644 grbl-lpc/flash.cpp diff --git a/grbl-lpc/flash.cpp b/grbl-lpc/flash.cpp new file mode 100644 index 0000000..0355d04 --- /dev/null +++ b/grbl-lpc/flash.cpp @@ -0,0 +1,94 @@ +// Copyright 2017 Todd Fleming +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "grbl.h" + +static constexpr unsigned flash_sector = 15; // Last 4k sector +static constexpr unsigned flash_addr = 0xF000; // Last 4k sector +static constexpr unsigned flash_size = 1024; // Only using 1k of a 4k sector +static char *flash_memory = (char *)flash_addr; // Flash memory +static char flash_buffer[flash_size] __attribute__((aligned(4))); // Copy of flash memory +using Iap = void(unsigned[], unsigned[]); // IAP entry point +static const Iap *iap = (Iap *)0x1FFF1FF1; // IAP entry point + +void eeprom_init() +{ + memcpy(flash_buffer, flash_memory, flash_size); +} + +void eeprom_commit() +{ + unsigned prepCommand[5] = { + 50, + flash_sector, + flash_sector, + }; + unsigned eraseCommand[5] = { + 52, + flash_sector, + flash_sector, + SystemCoreClock / 1000, + }; + unsigned writeCommand[5] = { + 51, + flash_addr, + (unsigned)flash_buffer, + flash_size, + SystemCoreClock / 1000, + }; + unsigned output[5]; + iap(prepCommand, output); + iap(eraseCommand, output); + iap(prepCommand, output); + iap(writeCommand, output); +} + +unsigned char eeprom_get_char(unsigned int addr) +{ + return flash_buffer[addr]; +} + +void eeprom_put_char(unsigned int addr, unsigned char new_value) +{ + flash_buffer[addr] = new_value; +} + +static unsigned char memcpy_with_checksum(char *dest, char *src, unsigned size) +{ + unsigned char checksum = 0; + while (size--) + { + checksum = ((checksum << 1) || (checksum >> 7)) + *src; + *dest++ = *src++; + } + return checksum; +} + +void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) +{ + auto checksum = memcpy_with_checksum(flash_buffer + destination, source, size); + flash_buffer[destination + size] = checksum; +} + +int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) +{ + auto checksum = memcpy_with_checksum(destination, flash_buffer + source, size); + return checksum == flash_buffer[source + size]; +} diff --git a/grbl/config.h b/grbl/config.h index bbbe413..25f4874 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -555,6 +555,10 @@ // job. At this time, this option only forces a planner buffer sync with these g-code commands. #define FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE // Default enabled. Comment to disable. +// LPC176x flash blocks have a rating of 10,000 write cycles. To prevent excess wear, we don't +// write G10, G28.1, and G30.1. Uncomment to enable these writes. +// #define STORE_COORD_DATA // Default disabled. Uncomment to enable. + // In Grbl v0.9 and prior, there is an old outstanding bug where the `WPos:` work position reported // may not correlate to what is executing, because `WPos:` is based on the g-code parser state, which // can be several motions behind. This option forces the planner buffer to empty, sync, and stop diff --git a/grbl/eeprom.c b/grbl/eeprom.c index 02caf20..c3382df 100644 --- a/grbl/eeprom.c +++ b/grbl/eeprom.c @@ -24,6 +24,8 @@ #include #include +#ifdef xxxxx // replaced by flash.cpp + /* These EEPROM bits have different names on different devices. */ #ifndef EEPE #define EEPE EEWE //!< EEPROM program/write enable. @@ -148,4 +150,6 @@ int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, uns return(checksum == eeprom_get_char(source)); } +#endif // xxxxx + // end of file diff --git a/grbl/eeprom.h b/grbl/eeprom.h index c9718a2..2fdc44e 100644 --- a/grbl/eeprom.h +++ b/grbl/eeprom.h @@ -25,5 +25,7 @@ unsigned char eeprom_get_char(unsigned int addr); void eeprom_put_char(unsigned int addr, unsigned 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); +void eeprom_init(); +void eeprom_commit(); #endif diff --git a/grbl/gcode.c b/grbl/gcode.c index e17bd77..6c04e22 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -992,7 +992,7 @@ uint8_t gc_execute_line(char *line) // [19. Go to predefined position, Set G10, or Set axis offsets ]: switch(gc_block.non_modal_command) { case NON_MODAL_SET_COORDINATE_DATA: - settings_write_coord_data(coord_select,gc_block.values.ijk); + settings_write_coord_data(coord_select,gc_block.values.ijk,false,true); // Update system coordinate system if currently active. if (gc_state.modal.coord_select == coord_select) { memcpy(gc_state.coord_system,gc_block.values.ijk,N_AXIS*sizeof(float)); @@ -1008,10 +1008,10 @@ uint8_t gc_execute_line(char *line) memcpy(gc_state.position, gc_block.values.ijk, N_AXIS*sizeof(float)); break; case NON_MODAL_SET_HOME_0: - settings_write_coord_data(SETTING_INDEX_G28,gc_state.position); + settings_write_coord_data(SETTING_INDEX_G28,gc_state.position,false,true); break; case NON_MODAL_SET_HOME_1: - settings_write_coord_data(SETTING_INDEX_G30,gc_state.position); + settings_write_coord_data(SETTING_INDEX_G30,gc_state.position,false,true); break; case NON_MODAL_SET_COORDINATE_OFFSET: memcpy(gc_state.coord_offset,gc_block.values.xyz,sizeof(gc_block.values.xyz)); diff --git a/grbl/main.c b/grbl/main.c index 8da129c..5b6feee 100644 --- a/grbl/main.c +++ b/grbl/main.c @@ -80,6 +80,7 @@ int main(void) isr_init(); // Set ISR priorities delay_init(); // Setup delay timer serial_init(); // Setup serial baud rate and interrupts + eeprom_init(); // Init EEPROM or FLASH settings_init(); // Load Grbl settings from EEPROM current_init(); // Configure stepper driver current stepper_init(); // Configure stepper pins and interrupt timers diff --git a/grbl/settings.c b/grbl/settings.c index f19c0a7..9f1e05f 100644 --- a/grbl/settings.c +++ b/grbl/settings.c @@ -32,6 +32,7 @@ void settings_store_startup_line(uint8_t n, char *line) #endif uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); + eeprom_commit(); } @@ -41,26 +42,36 @@ void settings_store_build_info(char *line) { // Build info can only be stored when state is IDLE. memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE); + eeprom_commit(); } // Method to store coord data parameters into EEPROM -void settings_write_coord_data(uint8_t coord_select, float *coord_data) +void settings_write_coord_data(uint8_t coord_select, float *coord_data, bool force, bool commit) { #ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE protocol_buffer_synchronize(); #endif - uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); + #ifdef STORE_COORD_DATA + force = true; + #endif + if(force) { + uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); + if(commit) + eeprom_commit(); + } } // Method to store Grbl global settings struct and version number into EEPROM // NOTE: This function can only be called in IDLE state. -void write_global_settings() +void write_global_settings(bool commit) { eeprom_put_char(0, SETTINGS_VERSION); memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); + if(commit) + eeprom_commit(); } @@ -107,14 +118,14 @@ void settings_restore(uint8_t restore_flag) { settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); - write_global_settings(); + write_global_settings(false); } if (restore_flag & SETTINGS_RESTORE_PARAMETERS) { uint8_t idx; float coord_data[N_AXIS]; memset(&coord_data, 0, sizeof(coord_data)); - for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); } + for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data, true, false); } } if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) { @@ -132,6 +143,8 @@ void settings_restore(uint8_t restore_flag) { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); eeprom_put_char(EEPROM_ADDR_BUILD_INFO+1 , 0); // Checksum } + + eeprom_commit(); } @@ -169,7 +182,7 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { // Reset with default zero vector clear_vector_float(coord_data); - settings_write_coord_data(coord_select,coord_data); + settings_write_coord_data(coord_select,coord_data,true,true); return(false); } return(true); @@ -301,7 +314,7 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) { return(STATUS_INVALID_STATEMENT); } } - write_global_settings(); + write_global_settings(true); return(STATUS_OK); } @@ -309,9 +322,9 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) { // Initialize the config subsystem void settings_init() { if(!read_global_settings()) { - //report_status_message(STATUS_SETTING_READ_FAIL); + report_status_message(STATUS_SETTING_READ_FAIL); settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data. - //report_grbl_settings(); + report_grbl_settings(); } } diff --git a/grbl/settings.h b/grbl/settings.h index 213d46b..e04de26 100644 --- a/grbl/settings.h +++ b/grbl/settings.h @@ -126,7 +126,7 @@ void settings_store_build_info(char *line); uint8_t settings_read_build_info(char *line); // Writes selected coordinate data to EEPROM -void settings_write_coord_data(uint8_t coord_select, float *coord_data); +void settings_write_coord_data(uint8_t coord_select, float *coord_data, bool force, bool commit); // Reads selected coordinate data from EEPROM uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data); diff --git a/lpc17xx/lpc1769.ld b/lpc17xx/lpc1769.ld index 9add023..9f0938c 100644 --- a/lpc17xx/lpc1769.ld +++ b/lpc17xx/lpc1769.ld @@ -13,15 +13,18 @@ MEMORY /* On-chip SRAM is a readable (r), writable (w) and */ /* executable region (x) */ - /* Main ROM region - 512k for LPC1768 */ - IROM (rx) : ORIGIN = 0x00000000, LENGTH = 512k + /* vector table */ + VECT (rx) : ORIGIN = 0x00000000, LENGTH = 4k - /* local static RAM - 32k for LPC1756 */ - IRAM0 (rwx) : ORIGIN = 0x10000000, LENGTH = 32k + /* ROM. Skips the remaining 4k flash sectors so they can be used for persistent data */ + IROM (rx) : ORIGIN = 0x00010000, LENGTH = 0x70000 /* 512k - 0x10000 */ - /* AHB SRAM - 16k for LPC1756 - often used for USB */ - IRAM1 (rwx) : ORIGIN = 0x2007C000, LENGTH = 16k - IRAM2 (rwx) : ORIGIN = 0x20080000, LENGTH = 16k + /* local static RAM - 32k for LPC1756 */ + IRAM0 (rwx) : ORIGIN = 0x10000000, LENGTH = 32736 /* 32k-32: 32 bytes at top reserved by IAP */ + + /* AHB SRAM - 16k for LPC1756 - often used for USB */ + IRAM1 (rwx) : ORIGIN = 0x2007C000, LENGTH = 16k + IRAM2 (rwx) : ORIGIN = 0x20080000, LENGTH = 16k } /* SECTION command : Define mapping of input sections */ @@ -32,11 +35,14 @@ SECTIONS /******************************************/ /* code section */ - /* "normal" code */ - - .text : + .isr_vector_section : { KEEP(*(.isr_vector .isr_vector.*)) + } >VECT + + /* "normal" code */ + .text : + { *(.text .text.*) *(.gnu.linkonce.t.*) *(.glue_7)