/* Speeduino - Simple engine management for the Arduino Mega 2560 platform Copyright (C) Josh Stewart A full copy of the license may be found in the projects root directory */ /** @file * Lower level ConfigPage*, Table2D, Table3D and EEPROM storage operations. */ #include "globals.h" #include EEPROM_LIB_H //This is defined in the board .h files #include "storage.h" #include "pages.h" #include "table3d_axis_io.h" #define EEPROM_DATA_VERSION 0 // Calibration data is stored at the end of the EEPROM (This is in case any further calibration tables are needed as they are large blocks) #define STORAGE_END 0xFFF // Should be E2END? #define EEPROM_CALIBRATION_CLT_VALUES (STORAGE_END-sizeof(cltCalibration_values)) #define EEPROM_CALIBRATION_CLT_BINS (EEPROM_CALIBRATION_CLT_VALUES-sizeof(cltCalibration_bins)) #define EEPROM_CALIBRATION_IAT_VALUES (EEPROM_CALIBRATION_CLT_BINS-sizeof(iatCalibration_values)) #define EEPROM_CALIBRATION_IAT_BINS (EEPROM_CALIBRATION_IAT_VALUES-sizeof(iatCalibration_bins)) #define EEPROM_CALIBRATION_O2_VALUES (EEPROM_CALIBRATION_IAT_BINS-sizeof(o2Calibration_values)) #define EEPROM_CALIBRATION_O2_BINS (EEPROM_CALIBRATION_O2_VALUES-sizeof(o2Calibration_bins)) #define EEPROM_LAST_BARO (EEPROM_CALIBRATION_O2_BINS-1) uint32_t deferEEPROMWritesUntil = 0; bool isEepromWritePending(void) { return BIT_CHECK(currentStatus.status4, BIT_STATUS4_BURNPENDING); } /** Write all config pages to EEPROM. */ void writeAllConfig(void) { uint8_t pageCount = getPageCount(); uint8_t page = 1U; writeConfig(page); page = page + 1; while (page 0) { canWrite = (counter <= write_block_size); } else { canWrite = (counter <= (write_block_size * 8)); } //Write to EEPROM more aggressively if the engine is not running return canWrite; } }; static inline write_location write_range(const byte *pStart, const byte *pEnd, write_location location) { while ( location.can_write() && pStart!=pEnd) { location.update(*pStart); ++pStart; ++location; } return location; } static inline write_location write(const table_row_iterator &row, write_location location) { return write_range(&*row, row.end(), location); } static inline write_location write(table_value_iterator it, write_location location) { while (location.can_write() && !it.at_end()) { location = write(*it, location); ++it; } return location; } static inline write_location write(table_axis_iterator it, write_location location) { const table3d_axis_io_converter converter = get_table3d_axis_converter(it.get_domain()); while (location.can_write() && !it.at_end()) { location.update(converter.to_byte(*it)); ++location; ++it; } return location; } static inline write_location writeTable(void *pTable, table_type_t key, write_location location) { return write(y_rbegin(pTable, key), write(x_begin(pTable, key), write(rows_begin(pTable, key), location))); } //Simply an alias for EEPROM.update() void EEPROMWriteRaw(uint16_t address, uint8_t data) { EEPROM.update(address, data); } uint8_t EEPROMReadRaw(uint16_t address) { return EEPROM.read(address); } // ================================= End write support =============================== /** Write a table or map to EEPROM storage. Takes the current configuration (config pages and maps) and writes them to EEPROM as per the layout defined in storage.h. */ void writeConfig(uint8_t pageNum) { //The maximum number of write operations that will be performed in one go. //If we try to write to the EEPROM too fast (Eg Each write takes ~3ms on the AVR) then //the rest of the system can hang) #if defined(USE_SPI_EEPROM) //For use with common Winbond SPI EEPROMs Eg W25Q16JV uint8_t EEPROM_MAX_WRITE_BLOCK = 20; //This needs tuning #elif defined(CORE_STM32) || defined(CORE_TEENSY) uint8_t EEPROM_MAX_WRITE_BLOCK = 64; #else uint8_t EEPROM_MAX_WRITE_BLOCK = 18; if(BIT_CHECK(currentStatus.status4, BIT_STATUS4_COMMS_COMPAT)) { EEPROM_MAX_WRITE_BLOCK = 8; } //If comms compatibility mode is on, slow the burn rate down even further #ifdef CORE_AVR //In order to prevent missed pulses during EEPROM writes on AVR, scale the //maximum write block size based on the RPM. //This calculation is based on EEPROM writes taking approximately 4ms per byte //(Actual value is 3.8ms, so 4ms has some safety margin) if(currentStatus.RPM > 65) //Min RPM of 65 prevents overflow of uint8_t { EEPROM_MAX_WRITE_BLOCK = (uint8_t)(15000U / currentStatus.RPM); EEPROM_MAX_WRITE_BLOCK = max(EEPROM_MAX_WRITE_BLOCK, 1); EEPROM_MAX_WRITE_BLOCK = min(EEPROM_MAX_WRITE_BLOCK, 15); //Any higher than this will cause comms timeouts on AVR } #endif #endif write_location result = { 0, 0, EEPROM_MAX_WRITE_BLOCK }; switch(pageNum) { case veMapPage: /*--------------------------------------------------- | Fuel table (See storage.h for data layout) - Page 1 | 16x16 table itself + the 16 values along each of the axis -----------------------------------------------------*/ result = writeTable(&fuelTable, decltype(fuelTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG1_MAP)); break; case veSetPage: /*--------------------------------------------------- | Config page 2 (See storage.h for data layout) | 64 byte long config table -----------------------------------------------------*/ result = write_range((byte *)&configPage2, (byte *)&configPage2+sizeof(configPage2), result.changeWriteAddress(EEPROM_CONFIG2_START)); break; case ignMapPage: /*--------------------------------------------------- | Ignition table (See storage.h for data layout) - Page 1 | 16x16 table itself + the 16 values along each of the axis -----------------------------------------------------*/ result = writeTable(&ignitionTable, decltype(ignitionTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG3_MAP)); break; case ignSetPage: /*--------------------------------------------------- | Config page 2 (See storage.h for data layout) | 64 byte long config table -----------------------------------------------------*/ result = write_range((byte *)&configPage4, (byte *)&configPage4+sizeof(configPage4), result.changeWriteAddress(EEPROM_CONFIG4_START)); break; case afrMapPage: /*--------------------------------------------------- | AFR table (See storage.h for data layout) - Page 5 | 16x16 table itself + the 16 values along each of the axis -----------------------------------------------------*/ result = writeTable(&afrTable, decltype(afrTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG5_MAP)); break; case afrSetPage: /*--------------------------------------------------- | Config page 3 (See storage.h for data layout) | 64 byte long config table -----------------------------------------------------*/ result = write_range((byte *)&configPage6, (byte *)&configPage6+sizeof(configPage6), result.changeWriteAddress(EEPROM_CONFIG6_START)); break; case boostvvtPage: /*--------------------------------------------------- | Boost and vvt tables (See storage.h for data layout) - Page 8 | 8x8 table itself + the 8 values along each of the axis -----------------------------------------------------*/ result = writeTable(&boostTable, decltype(boostTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG7_MAP1)); result = writeTable(&vvtTable, decltype(vvtTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG7_MAP2)); result = writeTable(&stagingTable, decltype(stagingTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG7_MAP3)); break; case seqFuelPage: /*--------------------------------------------------- | Fuel trim tables (See storage.h for data layout) - Page 9 | 6x6 tables itself + the 6 values along each of the axis -----------------------------------------------------*/ result = writeTable(&trim1Table, decltype(trim1Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP1)); result = writeTable(&trim2Table, decltype(trim2Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP2)); result = writeTable(&trim3Table, decltype(trim3Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP3)); result = writeTable(&trim4Table, decltype(trim4Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP4)); result = writeTable(&trim5Table, decltype(trim5Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP5)); result = writeTable(&trim6Table, decltype(trim6Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP6)); result = writeTable(&trim7Table, decltype(trim7Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP7)); result = writeTable(&trim8Table, decltype(trim8Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG8_MAP8)); break; case canbusPage: /*--------------------------------------------------- | Config page 10 (See storage.h for data layout) | 192 byte long config table -----------------------------------------------------*/ result = write_range((byte *)&configPage9, (byte *)&configPage9+sizeof(configPage9), result.changeWriteAddress(EEPROM_CONFIG9_START)); break; case warmupPage: /*--------------------------------------------------- | Config page 11 (See storage.h for data layout) | 192 byte long config table -----------------------------------------------------*/ result = write_range((byte *)&configPage10, (byte *)&configPage10+sizeof(configPage10), result.changeWriteAddress(EEPROM_CONFIG10_START)); break; case fuelMap2Page: /*--------------------------------------------------- | Fuel table 2 (See storage.h for data layout) | 16x16 table itself + the 16 values along each of the axis -----------------------------------------------------*/ result = writeTable(&fuelTable2, decltype(fuelTable2)::type_key, result.changeWriteAddress(EEPROM_CONFIG11_MAP)); break; case wmiMapPage: /*--------------------------------------------------- | WMI and Dwell tables (See storage.h for data layout) - Page 12 | 8x8 WMI table itself + the 8 values along each of the axis | 8x8 VVT2 table + the 8 values along each of the axis | 4x4 Dwell table itself + the 4 values along each of the axis -----------------------------------------------------*/ result = writeTable(&wmiTable, decltype(wmiTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP)); result = writeTable(&vvt2Table, decltype(vvt2Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP2)); result = writeTable(&dwellTable, decltype(dwellTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP3)); break; case progOutsPage: /*--------------------------------------------------- | Config page 13 (See storage.h for data layout) -----------------------------------------------------*/ result = write_range((byte *)&configPage13, (byte *)&configPage13+sizeof(configPage13), result.changeWriteAddress(EEPROM_CONFIG13_START)); break; case ignMap2Page: /*--------------------------------------------------- | Ignition table (See storage.h for data layout) - Page 1 | 16x16 table itself + the 16 values along each of the axis -----------------------------------------------------*/ result = writeTable(&ignitionTable2, decltype(ignitionTable2)::type_key, result.changeWriteAddress(EEPROM_CONFIG14_MAP)); break; case boostvvtPage2: /*--------------------------------------------------- | Boost duty cycle lookuptable (See storage.h for data layout) - Page 15 | 8x8 table itself + the 8 values along each of the axis -----------------------------------------------------*/ result = writeTable(&boostTableLookupDuty, decltype(boostTableLookupDuty)::type_key, result.changeWriteAddress(EEPROM_CONFIG15_MAP)); /*--------------------------------------------------- | Config page 15 (See storage.h for data layout) -----------------------------------------------------*/ result = write_range((byte *)&configPage15, (byte *)&configPage15+sizeof(configPage15), result.changeWriteAddress(EEPROM_CONFIG15_START)); break; default: break; } BIT_WRITE(currentStatus.status4, BIT_STATUS4_BURNPENDING, !result.can_write()); } /** Reset all configPage* structs (2,4,6,9,10,13) and write them full of null-bytes. */ void resetConfigPages(void) { for (uint8_t page=1; page