From 5be2559eda5d6bae484104831e43b1b0fac8517c Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 14 Aug 2018 07:19:34 +0100 Subject: [PATCH] Use flash memory to emulate EEPROM (#11500) Use a sector of the LPC flash memory to emulate EEPROM storage, removing the need to have an SD card to store system parameters. --- .../HAL/HAL_LPC1768/persistent_store_api.h | 24 +++ .../HAL_LPC1768/persistent_store_flash.cpp | 139 ++++++++++++++++++ .../HAL_LPC1768/persistent_store_sdcard.cpp | 80 +++++----- frameworks/CMSIS/LPC1768/system/LPC1768.ld | 6 +- 4 files changed, 208 insertions(+), 41 deletions(-) create mode 100644 Marlin/src/HAL/HAL_LPC1768/persistent_store_api.h create mode 100644 Marlin/src/HAL/HAL_LPC1768/persistent_store_flash.cpp diff --git a/Marlin/src/HAL/HAL_LPC1768/persistent_store_api.h b/Marlin/src/HAL/HAL_LPC1768/persistent_store_api.h new file mode 100644 index 000000000..2b4d23a54 --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/persistent_store_api.h @@ -0,0 +1,24 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ +#include "../persistent_store_api.h" + +//#define FLASH_EEPROM diff --git a/Marlin/src/HAL/HAL_LPC1768/persistent_store_flash.cpp b/Marlin/src/HAL/HAL_LPC1768/persistent_store_flash.cpp new file mode 100644 index 000000000..36825f15f --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/persistent_store_flash.cpp @@ -0,0 +1,139 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ +#ifdef TARGET_LPC1768 + +/** + * Emulate EEPROM storage using Flash Memory + * + * Use a single 32K flash sector to store EEPROM data. To reduce the + * number of erase operations a simple "levelling" scheme is used that + * maintains a number of EEPROM "slots" within the larger flash sector. + * Each slot is used in turn and the entire sector is only erased when all + * slots have been used. + * + * A simple RAM image is used to hold the EEPROM data during I/O operations + * and this is flushed to the next available slot when an update is complete. + * If RAM usage becomes an issue we could store this image in one of the two + * 16Kb I/O buffers (intended to hold DMA USB and Ethernet data, but currently + * unused). + */ +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(EEPROM_SETTINGS) + +#include "persistent_store_api.h" + +#if ENABLED(FLASH_EEPROM) + +extern "C" { + #include "lpc17xx_iap.h" +} + +#define SECTOR_START(sector) ((sector < 16) ? (sector * 0x1000) : ((sector - 14) * 0x8000)) +#define EEPROM_SECTOR 29 +#define EEPROM_SIZE (E2END+1) +#define SECTOR_SIZE (32768) +#define EEPROM_SLOTS (SECTOR_SIZE/EEPROM_SIZE) +#define EEPROM_ERASE (0xff) +#define SLOT_ADDRESS(sector, slot) (((uint8_t *)SECTOR_START(sector)) + slot * EEPROM_SIZE) + +#if EEPROM_SIZE != 4096 + #error "EEPROM_SIZE must match flash write size" +#endif + +static uint8_t ram_eeprom[EEPROM_SIZE]; +static bool eeprom_dirty = false; +static int current_slot = 0; + +bool PersistentStore::access_start() { + uint32_t first_nblank_loc, first_nblank_val; + IAP_STATUS_CODE status; + + // discover which slot we are currently using. + __disable_irq(); + status = BlankCheckSector(EEPROM_SECTOR, EEPROM_SECTOR, &first_nblank_loc, &first_nblank_val); + __enable_irq(); + SERIAL_PROTOCOLLNPAIR("Blank check status: ", status); + if (status == CMD_SUCCESS) { + // sector is blank so nothing stored yet + SERIAL_PROTOCOLLNPGM("FLASH empty"); + for (int i = 0; i < EEPROM_SIZE; i++) ram_eeprom[i] = EEPROM_ERASE; + current_slot = EEPROM_SLOTS; + } + else { + // current slot is the first non blank one + current_slot = first_nblank_loc / EEPROM_SIZE; + SERIAL_PROTOCOLLNPAIR("Flash slot: ", current_slot); + uint8_t *eeprom_data = SLOT_ADDRESS(EEPROM_SECTOR, current_slot); + SERIAL_PROTOCOLLNPAIR("Address: ", (int)eeprom_data); + + // load current settings + for (int i = 0; i < EEPROM_SIZE; i++) ram_eeprom[i] = eeprom_data[i]; + } + eeprom_dirty = false; + + return true; +} + +bool PersistentStore::access_finish() { + if (eeprom_dirty) { + IAP_STATUS_CODE status; + if (--current_slot < 0) { + // all slots have been used, erase everything and start again + __disable_irq(); + PrepareSector(EEPROM_SECTOR, EEPROM_SECTOR); + status = EraseSector(EEPROM_SECTOR, EEPROM_SECTOR); + __enable_irq(); + SERIAL_PROTOCOLLNPAIR("Erase status: ", status); + current_slot = EEPROM_SLOTS - 1; + } + SERIAL_PROTOCOLLNPAIR("Writing data to: ", current_slot); + __disable_irq(); + PrepareSector(EEPROM_SECTOR, EEPROM_SECTOR); + status = CopyRAM2Flash(SLOT_ADDRESS(EEPROM_SECTOR, current_slot), ram_eeprom, IAP_WRITE_4096); + __enable_irq(); + SERIAL_PROTOCOLLNPAIR("CopyRAM2Flash status: ", status); + if (status != CMD_SUCCESS) return false; + eeprom_dirty = false; + } + return true; +} + +bool PersistentStore::write_data(int &pos, const uint8_t *value, uint16_t size, uint16_t *crc) { + for (int i = 0; i < size; i++) ram_eeprom[pos + i] = value[i]; + eeprom_dirty = true; + crc16(crc, value, size); + pos += size; + return false; // return true for any error +} + +bool PersistentStore::read_data(int &pos, uint8_t* value, uint16_t size, uint16_t *crc, const bool writing/*=true*/) { + const uint8_t * const buff = writing ? &value[0] : &ram_eeprom[pos]; + if (writing) for (int i = 0; i < size; i++) value[i] = ram_eeprom[pos + i]; + crc16(crc, buff, size); + pos += size; + return false; // return true for any error +} + +#endif // FLASH_EEPROM +#endif // EEPROM_SETTINGS +#endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/HAL_LPC1768/persistent_store_sdcard.cpp b/Marlin/src/HAL/HAL_LPC1768/persistent_store_sdcard.cpp index 42264e1cc..f221f1b0b 100644 --- a/Marlin/src/HAL/HAL_LPC1768/persistent_store_sdcard.cpp +++ b/Marlin/src/HAL/HAL_LPC1768/persistent_store_sdcard.cpp @@ -22,11 +22,13 @@ */ #ifdef TARGET_LPC1768 -#include "../../inc/MarlinConfig.h" +#include "../../inc/MarlinConfigPre.h" #if ENABLED(EEPROM_SETTINGS) -#include "../persistent_store_api.h" +#include "persistent_store_api.h" + +#if DISABLED(FLASH_EEPROM) #include #include @@ -73,8 +75,31 @@ bool PersistentStore::access_finish() { return true; } -// File function return codes for type FRESULT This goes away soon. But it is helpful right now to see -// the different errors the read_data() and write_data() functions are seeing. +// This extra chit-chat goes away soon, but is helpful for now +// to see errors that are happening in read_data / write_data +static void debug_rw(const bool write, int &pos, const uint8_t *value, const size_t size, const FRESULT s, const size_t total=0) { + const char * const rw_str = write ? PSTR("write") : PSTR("read"); + SERIAL_PROTOCOLCHAR(' '); + serialprint_PGM(rw_str); + SERIAL_PROTOCOLPAIR("_data(", pos); + SERIAL_PROTOCOLPAIR(",", (int)value); + SERIAL_PROTOCOLPAIR(",", (int)size); + SERIAL_PROTOCOLLNPGM(", ...)"); + if (total) { + SERIAL_PROTOCOLPGM(" f_"); + serialprint_PGM(rw_str); + SERIAL_PROTOCOLPAIR("()=", (int)s); + SERIAL_PROTOCOLPAIR("\n size=", size); + SERIAL_PROTOCOLPGM("\n bytes_"); + serialprint_PGM(write ? PSTR("written=") : PSTR("read=")); + SERIAL_PROTOCOLLN(total); + } + else + SERIAL_PROTOCOLLNPAIR(" f_lseek()=", (int)s); +} + +// File function return codes for type FRESULT. This goes away soon, but +// is helpful right now to see any errors in read_data and write_data. // // typedef enum { // FR_OK = 0, /* (0) Succeeded */ @@ -106,28 +131,18 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, const size_t si s = f_lseek(&eeprom_file, pos); if (s) { - SERIAL_PROTOCOLPAIR(" write_data(", pos); // This extra chit-chat goes away soon. But it is helpful - SERIAL_PROTOCOLPAIR(",", (int)value); // right now to see errors that are happening in the - SERIAL_PROTOCOLPAIR(",", (int)size); // read_data() and write_data() functions - SERIAL_PROTOCOLLNPGM("...)"); - SERIAL_PROTOCOLLNPAIR(" f_lseek()=", (int)s); - return s; + debug_rw(true, pos, value, size, s); + return s; } s = f_write(&eeprom_file, (void*)value, size, &bytes_written); if (s) { - SERIAL_PROTOCOLPAIR(" write_data(", pos); // This extra chit-chat goes away soon. But it is helpful - SERIAL_PROTOCOLPAIR(",", (int)value); // right now to see errors that are happening in the - SERIAL_PROTOCOLPAIR(",", size); // read_data() and write_data() functions - SERIAL_PROTOCOLLNPGM("...)"); - SERIAL_PROTOCOLLNPAIR(" f_write()=", (int)s); - SERIAL_PROTOCOLPAIR(" size=", size); - SERIAL_PROTOCOLLNPAIR("\n bytes_written=", bytes_written); - return s; + debug_rw(true, pos, value, size, s, bytes_written); + return s; } crc16(crc, value, size); - pos = pos + size; - return (bytes_written != size); // return true for any error + pos += size; + return bytes_written != size; // return true for any error } bool PersistentStore::read_data(int &pos, uint8_t* value, const size_t size, uint16_t *crc, const bool writing/*=true*/) { @@ -137,14 +152,8 @@ bool PersistentStore::read_data(int &pos, uint8_t* value, const size_t size, uin s = f_lseek(&eeprom_file, pos); if (s) { - SERIAL_PROTOCOLPAIR(" read_data(", pos); // This extra chit-chat goes away soon. But it is helpful - SERIAL_PROTOCOLCHAR(','); - SERIAL_PROTOCOL((int)value); // right now to see errors that are happening in the - SERIAL_PROTOCOLCHAR(','); - SERIAL_PROTOCOL(size); // read_data() and write_data() functions - SERIAL_PROTOCOLLNPGM("...)"); - SERIAL_PROTOCOLLNPAIR(" f_lseek()=", (int)s); - return true; + debug_rw(false, pos, value, size, s); + return true; } if (writing) { @@ -158,23 +167,16 @@ bool PersistentStore::read_data(int &pos, uint8_t* value, const size_t size, uin } if (s) { - SERIAL_PROTOCOLPAIR(" read_data(", pos); // This extra chit-chat goes away soon. But it is helpful - SERIAL_PROTOCOLCHAR(','); - SERIAL_PROTOCOL((int)value); // right now to see errors that are happening in the - SERIAL_PROTOCOLCHAR(','); - SERIAL_PROTOCOL(size); // read_data() and write_data() functions - SERIAL_PROTOCOLLNPGM("...)"); - SERIAL_PROTOCOLLNPAIR(" f_write()=", (int)s); - SERIAL_PROTOCOLPAIR(" size=", size); - SERIAL_PROTOCOLLNPAIR("\n bytes_read=", bytes_read); - return true; + debug_rw(false, pos, value, size, s, bytes_read); + return true; } - pos = pos + size; + pos += size; return bytes_read != size; // return true for any error } const size_t PersistentStore::capacity() { return 4096; } // 4KiB of Emulated EEPROM +#endif // !FLASH_EEPROM #endif // EEPROM_SETTINGS #endif // TARGET_LPC1768 diff --git a/frameworks/CMSIS/LPC1768/system/LPC1768.ld b/frameworks/CMSIS/LPC1768/system/LPC1768.ld index 7236f0401..f30ea0d1f 100644 --- a/frameworks/CMSIS/LPC1768/system/LPC1768.ld +++ b/frameworks/CMSIS/LPC1768/system/LPC1768.ld @@ -1,8 +1,10 @@ /* Linker script for mbed LPC1768 */ MEMORY { - //FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K - FLASH (rx) : ORIGIN = 16K, LENGTH = (512K - 16K) + /* Reserve first 16K (4 sectors * 4KB) for bootloader + * Reserve the last 32KB sector for EEPROM emulation + */ + FLASH (rx) : ORIGIN = 16K, LENGTH = (512K - 48K) RAM (rwx) : ORIGIN = 0x100000C8, LENGTH = (32K - 0xC8) USB_RAM(rwx) : ORIGIN = 0x2007C000, LENGTH = 16K