mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-14 20:10:18 +03:00
Support load/save configuration to external flash
Fixes to 6a3e7d8e6 as the flash partition code had changed during the merge to master.
This commit is contained in:
parent
a6c07b046b
commit
cf1ce1a67b
7 changed files with 189 additions and 50 deletions
1
Makefile
1
Makefile
|
@ -677,6 +677,5 @@ check-platform-included:
|
||||||
# rebuild everything when makefile changes
|
# rebuild everything when makefile changes
|
||||||
$(TARGET_OBJS): Makefile $(TARGET_DIR)/target.mk $(wildcard make/*)
|
$(TARGET_OBJS): Makefile $(TARGET_DIR)/target.mk $(wildcard make/*)
|
||||||
|
|
||||||
|
|
||||||
# include auto-generated dependencies
|
# include auto-generated dependencies
|
||||||
-include $(TARGET_DEPS)
|
-include $(TARGET_DEPS)
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "pg/pg.h"
|
#include "pg/pg.h"
|
||||||
#include "fc/config.h"
|
#include "fc/config.h"
|
||||||
|
|
||||||
|
#include "drivers/flash.h"
|
||||||
#include "drivers/system.h"
|
#include "drivers/system.h"
|
||||||
|
|
||||||
static uint16_t eepromConfigSize;
|
static uint16_t eepromConfigSize;
|
||||||
|
@ -78,6 +79,35 @@ typedef struct {
|
||||||
uint32_t word;
|
uint32_t word;
|
||||||
} PG_PACKED packingTest_t;
|
} PG_PACKED packingTest_t;
|
||||||
|
|
||||||
|
#ifdef EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
bool loadEEPROMFromExternalFlash(void)
|
||||||
|
{
|
||||||
|
const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
|
||||||
|
const flashGeometry_t *flashGeometry = flashGetGeometry();
|
||||||
|
|
||||||
|
uint32_t flashStartAddress = flashPartition->startSector * flashGeometry->sectorSize;
|
||||||
|
|
||||||
|
uint32_t totalBytesRead = 0;
|
||||||
|
uint32_t bytesRead = 0;
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytesRead = flashReadBytes(flashStartAddress + totalBytesRead, &eepromData[totalBytesRead], EEPROM_SIZE - totalBytesRead);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
success = (totalBytesRead == EEPROM_SIZE);
|
||||||
|
} while (!success && bytesRead);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EEPROM_IN_FILE
|
||||||
|
void loadEEPROMFromFile(void) {
|
||||||
|
FLASH_Unlock(); // load existing config file into eepromData
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void initEEPROM(void)
|
void initEEPROM(void)
|
||||||
{
|
{
|
||||||
// Verify that this architecture packs as expected.
|
// Verify that this architecture packs as expected.
|
||||||
|
@ -89,7 +119,15 @@ void initEEPROM(void)
|
||||||
STATIC_ASSERT(sizeof(configRecord_t) == 6, record_size_failed);
|
STATIC_ASSERT(sizeof(configRecord_t) == 6, record_size_failed);
|
||||||
|
|
||||||
#ifdef EEPROM_IN_FILE
|
#ifdef EEPROM_IN_FILE
|
||||||
FLASH_Unlock(); // load existing config file into eepromData
|
loadEEPROMFromFile();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
bool eepromLoaded = loadEEPROMFromExternalFlash();
|
||||||
|
if (!eepromLoaded) {
|
||||||
|
// Flash read failed - just die now
|
||||||
|
failureMode(FAILURE_FLASH_READ_FAILED);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +197,11 @@ uint16_t getEEPROMConfigSize(void)
|
||||||
|
|
||||||
size_t getEEPROMStorageSize(void)
|
size_t getEEPROMStorageSize(void)
|
||||||
{
|
{
|
||||||
|
#if defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
|
|
||||||
|
const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
|
||||||
|
return FLASH_PARTITION_SECTOR_COUNT(flashPartition) * flashGetGeometry()->sectorSize;
|
||||||
|
#endif
|
||||||
#ifdef EEPROM_IN_RAM
|
#ifdef EEPROM_IN_RAM
|
||||||
return EEPROM_SIZE;
|
return EEPROM_SIZE;
|
||||||
#else
|
#else
|
||||||
|
@ -268,9 +311,15 @@ void writeConfigToEEPROM(void)
|
||||||
for (int attempt = 0; attempt < 3 && !success; attempt++) {
|
for (int attempt = 0; attempt < 3 && !success; attempt++) {
|
||||||
if (writeSettingsToEEPROM()) {
|
if (writeSettingsToEEPROM()) {
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
|
#ifdef EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
// copy it back from flash to the in-memory buffer.
|
||||||
|
success = loadEEPROMFromExternalFlash();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (success && isEEPROMVersionValid() && isEEPROMStructureValid()) {
|
if (success && isEEPROMVersionValid() && isEEPROMStructureValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,13 @@
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include "drivers/system.h"
|
#include "drivers/system.h"
|
||||||
|
#include "drivers/flash.h"
|
||||||
|
|
||||||
#include "config/config_streamer.h"
|
#include "config/config_streamer.h"
|
||||||
|
|
||||||
|
#if defined(STM32H750xx) && !(defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_RAM))
|
||||||
|
#error "STM32750xx only has one flash page which contains the bootloader, no spare flash pages available, use external storage for persistent config or ram for target testing"
|
||||||
|
#endif
|
||||||
// @todo this is not strictly correct for F4/F7, where sector sizes are variable
|
// @todo this is not strictly correct for F4/F7, where sector sizes are variable
|
||||||
#if !defined(FLASH_PAGE_SIZE)
|
#if !defined(FLASH_PAGE_SIZE)
|
||||||
// F1
|
// F1
|
||||||
|
@ -74,12 +78,11 @@ void config_streamer_init(config_streamer_t *c)
|
||||||
|
|
||||||
void config_streamer_start(config_streamer_t *c, uintptr_t base, int size)
|
void config_streamer_start(config_streamer_t *c, uintptr_t base, int size)
|
||||||
{
|
{
|
||||||
// base must start at FLASH_PAGE_SIZE boundary
|
// base must start at FLASH_PAGE_SIZE boundary when using embedded flash.
|
||||||
c->address = base;
|
c->address = base;
|
||||||
c->size = size;
|
c->size = size;
|
||||||
if (!c->unlocked) {
|
if (!c->unlocked) {
|
||||||
|
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
#if defined(EEPROM_IN_RAM)
|
|
||||||
// NOP
|
// NOP
|
||||||
#elif defined(EEPROM_IN_FLASH) || defined(EEPROM_IN_FILE)
|
#elif defined(EEPROM_IN_FLASH) || defined(EEPROM_IN_FILE)
|
||||||
#if defined(STM32F7) || defined(STM32H7)
|
#if defined(STM32F7) || defined(STM32H7)
|
||||||
|
@ -91,7 +94,7 @@ void config_streamer_start(config_streamer_t *c, uintptr_t base, int size)
|
||||||
c->unlocked = true;
|
c->unlocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE)
|
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE) || defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
// NOP
|
// NOP
|
||||||
#elif defined(EEPROM_IN_FLASH)
|
#elif defined(EEPROM_IN_FLASH)
|
||||||
#if defined(STM32F10X)
|
#if defined(STM32F10X)
|
||||||
|
@ -113,7 +116,7 @@ void config_streamer_start(config_streamer_t *c, uintptr_t base, int size)
|
||||||
c->err = 0;
|
c->err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EEPROM_IN_RAM)
|
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
// No flash sector method required.
|
// No flash sector method required.
|
||||||
#elif defined(EEPROM_IN_FLASH)
|
#elif defined(EEPROM_IN_FLASH)
|
||||||
#if defined(STM32F745xx) || defined(STM32F746xx) || defined(STM32F765xx)
|
#if defined(STM32F745xx) || defined(STM32F746xx) || defined(STM32F765xx)
|
||||||
|
@ -342,12 +345,48 @@ static void getFLASHSectorForEEPROM(uint32_t *bank, uint32_t *sector)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// FIXME the return values are currently magic numbers
|
||||||
static int write_word(config_streamer_t *c, config_streamer_buffer_align_type_t *buffer)
|
static int write_word(config_streamer_t *c, config_streamer_buffer_align_type_t *buffer)
|
||||||
{
|
{
|
||||||
if (c->err != 0) {
|
if (c->err != 0) {
|
||||||
return c->err;
|
return c->err;
|
||||||
}
|
}
|
||||||
#if defined(EEPROM_IN_RAM)
|
#if defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
|
|
||||||
|
uint32_t dataOffset = (uint32_t)(c->address - (uintptr_t)&eepromData[0]);
|
||||||
|
|
||||||
|
const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
|
||||||
|
const flashGeometry_t *flashGeometry = flashGetGeometry();
|
||||||
|
|
||||||
|
uint32_t flashStartAddress = flashPartition->startSector * flashGeometry->sectorSize;
|
||||||
|
uint32_t flashOverflowAddress = ((flashPartition->endSector + 1) * flashGeometry->sectorSize); // +1 to sector for inclusive
|
||||||
|
|
||||||
|
uint32_t flashAddress = flashStartAddress + dataOffset;
|
||||||
|
if (flashAddress + CONFIG_STREAMER_BUFFER_SIZE > flashOverflowAddress) {
|
||||||
|
return -3; // address is past end of partition
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flashSectorSize = flashGeometry->sectorSize;
|
||||||
|
uint32_t flashPageSize = flashGeometry->pageSize;
|
||||||
|
|
||||||
|
bool onPageBoundary = (flashAddress % flashPageSize == 0);
|
||||||
|
if (onPageBoundary) {
|
||||||
|
|
||||||
|
bool firstPage = (flashAddress == flashStartAddress);
|
||||||
|
if (!firstPage) {
|
||||||
|
flashPageProgramFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flashAddress % flashSectorSize == 0) {
|
||||||
|
flashEraseSector(flashAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
flashPageProgramBegin(flashAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
flashPageProgramContinue((uint8_t *)buffer, CONFIG_STREAMER_BUFFER_SIZE);
|
||||||
|
|
||||||
|
#elif defined(EEPROM_IN_RAM)
|
||||||
if (c->address == (uintptr_t)&eepromData[0]) {
|
if (c->address == (uintptr_t)&eepromData[0]) {
|
||||||
memset(eepromData, 0, sizeof(eepromData));
|
memset(eepromData, 0, sizeof(eepromData));
|
||||||
}
|
}
|
||||||
|
@ -470,7 +509,9 @@ int config_streamer_flush(config_streamer_t *c)
|
||||||
int config_streamer_finish(config_streamer_t *c)
|
int config_streamer_finish(config_streamer_t *c)
|
||||||
{
|
{
|
||||||
if (c->unlocked) {
|
if (c->unlocked) {
|
||||||
#if defined(EEPROM_IN_RAM)
|
#if defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
|
flashFlush();
|
||||||
|
#elif defined(EEPROM_IN_RAM)
|
||||||
// NOP
|
// NOP
|
||||||
#elif defined(EEPROM_IN_FILE)
|
#elif defined(EEPROM_IN_FILE)
|
||||||
FLASH_Lock();
|
FLASH_Lock();
|
||||||
|
|
|
@ -26,7 +26,10 @@
|
||||||
// Streams data out to the EEPROM, padding to the write size as
|
// Streams data out to the EEPROM, padding to the write size as
|
||||||
// needed, and updating the checksum as it goes.
|
// needed, and updating the checksum as it goes.
|
||||||
|
|
||||||
#ifdef STM32H7
|
#ifdef EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
#define CONFIG_STREAMER_BUFFER_SIZE 8 // Must not be greater than the smallest flash page size of all compiled-in flash devices.
|
||||||
|
typedef uint32_t config_streamer_buffer_align_type_t;
|
||||||
|
#elif defined(STM32H7)
|
||||||
#define CONFIG_STREAMER_BUFFER_SIZE 32 // Flash word = 256-bits
|
#define CONFIG_STREAMER_BUFFER_SIZE 32 // Flash word = 256-bits
|
||||||
typedef uint64_t config_streamer_buffer_align_type_t;
|
typedef uint64_t config_streamer_buffer_align_type_t;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -32,7 +32,8 @@ typedef enum {
|
||||||
FAILURE_ACC_INCOMPATIBLE,
|
FAILURE_ACC_INCOMPATIBLE,
|
||||||
FAILURE_INVALID_EEPROM_CONTENTS,
|
FAILURE_INVALID_EEPROM_CONTENTS,
|
||||||
FAILURE_FLASH_WRITE_FAILED,
|
FAILURE_FLASH_WRITE_FAILED,
|
||||||
FAILURE_GYRO_INIT_FAILED
|
FAILURE_GYRO_INIT_FAILED,
|
||||||
|
FAILURE_FLASH_READ_FAILED
|
||||||
} failureMode_e;
|
} failureMode_e;
|
||||||
|
|
||||||
#define WARNING_FLASH_DURATION_MS 50
|
#define WARNING_FLASH_DURATION_MS 50
|
||||||
|
|
|
@ -206,6 +206,46 @@ static IO_t busSwitchResetPin = IO_NONE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void configureSPIAndQuadSPI(void)
|
||||||
|
{
|
||||||
|
#ifdef USE_SPI
|
||||||
|
spiPinConfigure(spiPinConfig(0));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sensorsPreInit();
|
||||||
|
|
||||||
|
#ifdef USE_SPI
|
||||||
|
spiPreinit();
|
||||||
|
|
||||||
|
#ifdef USE_SPI_DEVICE_1
|
||||||
|
spiInit(SPIDEV_1);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SPI_DEVICE_2
|
||||||
|
spiInit(SPIDEV_2);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SPI_DEVICE_3
|
||||||
|
spiInit(SPIDEV_3);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SPI_DEVICE_4
|
||||||
|
spiInit(SPIDEV_4);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SPI_DEVICE_5
|
||||||
|
spiInit(SPIDEV_5);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SPI_DEVICE_6
|
||||||
|
spiInit(SPIDEV_6);
|
||||||
|
#endif
|
||||||
|
#endif // USE_SPI
|
||||||
|
|
||||||
|
#ifdef USE_QUADSPI
|
||||||
|
quadSpiPinConfigure(quadSpiConfig(0));
|
||||||
|
|
||||||
|
#ifdef USE_QUADSPI_DEVICE_1
|
||||||
|
quadSpiInit(QUADSPIDEV_1);
|
||||||
|
#endif
|
||||||
|
#endif // USE_QUAD_SPI
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void init(void)
|
void init(void)
|
||||||
{
|
{
|
||||||
|
@ -252,6 +292,43 @@ void init(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
|
||||||
|
//
|
||||||
|
// Config on external flash presents an issue with pin configuration since the pin and flash configs for the
|
||||||
|
// external flash are in the config which is on a chip which we can't read yet!
|
||||||
|
//
|
||||||
|
// FIXME We need to add configuration into the bootloader image that can be read by the firmware.
|
||||||
|
// it's currently possible firmware and bootloader to become mismatched if the user changes them.
|
||||||
|
// This would cause undefined behaviour once the config is loaded. so for now, users must NOT change flash/pin configs needed for
|
||||||
|
// the system to boot and/or to save the config.
|
||||||
|
//
|
||||||
|
// note that target specific FLASH/SPI/QUADSPI configs are
|
||||||
|
// also not supported in USE_TARGET_CONFIG/targetConfigure() when using EEPROM_IN_EXTERNAL_FLASH.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// IMPORTANT: all default flash and pin configurations must be valid for the target after pgResetAll() is called.
|
||||||
|
// Target designers must ensure other devices connected the same SPI/QUADSPI interface as the flash chip do not
|
||||||
|
// cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI CS lines.
|
||||||
|
//
|
||||||
|
pgResetAll();
|
||||||
|
|
||||||
|
#ifdef TARGET_BUS_INIT
|
||||||
|
#error "EEPROM_IN_EXTERNAL_FLASH and TARGET_BUS_INIT are mutually exclusive"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
configureSPIAndQuadSPI();
|
||||||
|
|
||||||
|
#ifndef USE_FLASH_CHIP
|
||||||
|
#error "EEPROM_IN_EXTERNAL_FLASH requires USE_FLASH_CHIP to be defined."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
flashInit(flashConfig());
|
||||||
|
|
||||||
|
#endif // EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
|
||||||
|
|
||||||
initEEPROM();
|
initEEPROM();
|
||||||
|
|
||||||
ensureEEPROMStructureIsValid();
|
ensureEEPROMStructureIsValid();
|
||||||
|
@ -430,48 +507,15 @@ void init(void)
|
||||||
initInverters(serialPinConfig());
|
initInverters(serialPinConfig());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef TARGET_BUS_INIT
|
#ifdef TARGET_BUS_INIT
|
||||||
targetBusInit();
|
targetBusInit();
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef USE_SPI
|
#ifndef EEPROM_IN_EXTERNAL_FLASH
|
||||||
spiPinConfigure(spiPinConfig(0));
|
configureSPIAndQuadSPI();
|
||||||
#endif
|
#endif // EEPROM_IN_EXTERNAL_FLASH
|
||||||
|
|
||||||
sensorsPreInit();
|
|
||||||
|
|
||||||
#ifdef USE_SPI
|
|
||||||
spiPreinit();
|
|
||||||
|
|
||||||
#ifdef USE_SPI_DEVICE_1
|
|
||||||
spiInit(SPIDEV_1);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SPI_DEVICE_2
|
|
||||||
spiInit(SPIDEV_2);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SPI_DEVICE_3
|
|
||||||
spiInit(SPIDEV_3);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SPI_DEVICE_4
|
|
||||||
spiInit(SPIDEV_4);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SPI_DEVICE_5
|
|
||||||
spiInit(SPIDEV_5);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SPI_DEVICE_6
|
|
||||||
spiInit(SPIDEV_6);
|
|
||||||
#endif
|
|
||||||
#endif // USE_SPI
|
|
||||||
|
|
||||||
#ifdef USE_QUADSPI
|
|
||||||
quadSpiPinConfigure(quadSpiConfig(0));
|
|
||||||
|
|
||||||
#ifdef USE_QUADSPI_DEVICE_1
|
|
||||||
quadSpiInit(QUADSPIDEV_1);
|
|
||||||
#endif
|
|
||||||
#endif // USE_QUAD_SPI
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_USB_MSC
|
#ifdef USE_USB_MSC
|
||||||
/* MSC mode will start after init, but will not allow scheduler to run,
|
/* MSC mode will start after init, but will not allow scheduler to run,
|
||||||
|
@ -715,12 +759,14 @@ void init(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef EEPROM_IN_EXTERNAL_FLASH
|
||||||
#ifdef USE_FLASH_CHIP
|
#ifdef USE_FLASH_CHIP
|
||||||
flashInit(flashConfig());
|
flashInit(flashConfig());
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#ifdef USE_FLASHFS
|
#ifdef USE_FLASHFS
|
||||||
flashfsInit();
|
flashfsInit();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_BLACKBOX
|
#ifdef USE_BLACKBOX
|
||||||
#ifdef USE_SDCARD
|
#ifdef USE_SDCARD
|
||||||
|
|
|
@ -338,7 +338,7 @@
|
||||||
#undef USE_ESCSERIAL
|
#undef USE_ESCSERIAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE)
|
#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE) || defined(EEPROM_IN_EXTERNAL_FLASH)
|
||||||
#ifndef EEPROM_SIZE
|
#ifndef EEPROM_SIZE
|
||||||
#define EEPROM_SIZE 4096
|
#define EEPROM_SIZE 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue