Skip to content

EEPROM of the LGT8F328p : the important things you need to know

SuperUserNameMan edited this page Mar 10, 2021 · 5 revisions

EEPROM of the LGT8F382p : the important things you need to know

(to be updated once the lgt8fx v1.0.7 will be released)

Intro : two EEPROM libraries are available

The original EEPROM library provided by LGT is not compatible with the standard Arduino EEPROM library, and it is limited to 512 bytes of EEPROM which will "overrun to zero" (if you write from 512 to 1023, it will actually overwrite from 0 to 511).

The new EEPROM library keeps most compatibility with the original LGT EEPROM library, adds an extended C/C++ API, and adds the standard Arduino EEPROM object API. It is being tested here : https://github.com/dbuezas/lgt8fx/pull/64 (this PR has been merged already to the next release v1.0.7 that should be release soon)

It is specifically developed for the 328p variants, but basic support and backward compatibility have been added for 88a, 88d and 328d (despite it has not yet been tested on these specific MCU).

Because of the specificity of the LGT8Fx MCU, there will be some caveats and hints that you should be made aware of.

Summary :

  1. the EEPROM memory is emulated
  2. you can change the size of the emulated EEPROM
  3. each 1KB of emulated EEPROM occupies 2KB of main Flash memory
  4. for each 1KB of emulated EEPROM, only 1020 are available
  5. Increasing the size of the EEPROM will overwrite the boot-loader
  6. Increasing the size of the EEPROM might overwrite a part of your program
  7. The new EEPROM library
    7.1 the LGT EEPROM C/C++ API
    7.2 the LGT EEPROM object API
    7.3 the Arduino EEPROM object API
  8. LGT8Fx8-A and LGT8Fx8-D support

1) The EEPROM memory is emulated

There is no real EEPROM memory embedded.

Instead, it contains an hardware module whose purpose is to emulate EEPROM using the top of the main Flash memory.

This mean that the emulated EEPROM will share space with the program memory and the boot-loader.

2) You can change the size of the emulated EEPROM

By default, the size of this emulated EEPROM is set to 1KB, and it can be changed at run-time to 0KB, 1KB,2KB, 4K or 8KB.

However, changing its size at run-time might have some consequences that detailed below.

3) Each 1KB of emulated EEPROM occupies 2KB of main Flash memory

When a EEPROM/FLash memory cell is blank (filled with 0xFF), it is possible to write data directly into it. ( Only bits set to 0 can be written ).

So, when you want to update a non-blank cell of the emulated EEPROM, this cell has to be erased first. But, because the EEPROM is emulated using Flash memory, it is not possible to erase one single cell alone : The Flash memory is organized in page of 1KB, and only a full 1KB page can be erased.

To prevent data-loss, the controller of the emulated EEPROM uses an algorithm based on "page-flipping" : as a consequence, each 1KB of emulated EEPROM will use 2 pages of Flash memory.

One page is "current", and the other is "old".

Then, each time a memory cell needs to be updated, the controller erases the old page, copies the updated data from the current page to the old page, and flips them : the old page becomes the current page, and current page become the old page.

4) For each 1KB of emulated EEPROM, only 1020 are available

Because of the "page-flipping" algorithm, the emulated EEPROM controller has to keep track of which page is the current, and which page is the old. For this purpose, it uses the last cell of each 1KB page to write a flag. At the hardware level, on the 328p, each cell of the Flash memory is 32bits wide (4 bytes).

When the emulated EEPROM is set to 1KB (default), the only consequence is that only 1020 bytes will be available (instead of 1024).

However, when the emulated EEPROM is set to 2KB, 4KB or 8KB, the consequence is that EVERY last cell of EACH 1KB pages will be unavailable ! In other words : there will be holes into the memory space of the emulated EEPROM ; the emulated EEPROM memory space will not be continuous.

Neither the algorithm of the controller, nor the original LGT EEPROM library did provide any solution to skip these holes automatically.

But the new EEPROM library ( being tested here : https://github.com/dbuezas/lgt8fx/pull/64 ) will automatically skip these holes for you (like if the EEPROM space was continuous), unless you disable this feature.

number of 1KB pages of the EEPROM theoretical size of the EEPROM actual available bytes and emulated continuous space occupied space into main Flash memory beginning address into Flash memory
0 0 0 0 n/a
1 1024 1020 2048 0x7800
2 2048 2040 4096 0x7000
4 4096 4080 8192 0x6000
8 8192 8160 16384 0x4000
default 1024 1020 2048 0x7800

5) Increasing the size of the EEPROM will overwrite the boot-loader

The default bootloader is stored from 0x7400 to 0x77ff.

So, increasing the size of the EEPROM above 1KB will overwrite this boot-loader.

The only way to restore the boot-loader is using an ISP.

But if you have an ISP, you don't need the boot-loader anymore.

6) Increasing the size of the EEPROM might overwrite a part of your program

Larger emulated EEPROM will leave less space available into the Flash for your program. If you don't calculate correctly, they might overlap.

7) The new EEPROM library

As mentioned, the new EEPROM library is being tested here : https://github.com/dbuezas/lgt8fx/pull/64

It is written specifically for the LGT8F328p MCU.
Basic support has been added for LGT8F88a, 88d and 328d, and features specific to the 328p have been back-ported. However, it has not been tested on these variants so far.
If you encounter issues with other LGT MCU, please, let us know so we can improve compatibility.

This new library offers 3 API :

  • the slightly improved LGT EEPROM object API
  • the standard Arduino EEPROM object API
  • a lower level C/C++ API internally used by both EEPROM API implementations that you could use to optimize your code

The LGT C/C++ API will always be available when #include <EEPROM.h>.

But you will have to choose between the LGT EEPROM object API or the Arduino EEPROM object API (default).

If you want the LGT EEPROM object API, you will have to :

#define USE_LGT_EEPROM_API
#include <EEPROM.h>

7.1) the new LGT EEPROM C/C++ API

This EEPROM C/C++ API contains functions and macro used internally by the EEPROM classes of the LGT and Arduino EEPROM object API.

It contains the original LGT EEPROM C/C++ API, plus some improvements.

By default, this new improved version of the LGT EEPROM C/C++ API will automatically skip the holes at the end of each 1KB page. But you can disable this behaviour by setting the last parameter (namely real_address_mode) of each function to true.

// Change the size of the EEPROM at runtime :
// - valid values are : 0, 1, 2, 4 and 8.
// - /!\ This function will have no effect on 88a and 88d.
void lgt_eeprom_init( uint8_t number_of_1KB_pages = 1 ); 

// Return the current size of the EEPROM in bytes :
// - if theoretical is false (default), returns the number of bytes actually 
//  available for write (does not count the holes at the end of each 1KB page)
// - if theoretical is true, returns the theoretical size of the EEPROM 
// ( include the holes at the end of each 1KB page ).
int lgt_eeprom_size( bool theoretical = false );

// Converts a continuous space address to a real hardware address :
// - this helper function is used internally to automatically skip the holes 
//   at the end of each 1KB page.
uint16_t lgt_eeprom_continuous_address_to_real_address( uint16_t address );

// Read and write a byte :
// - if the last parameter is false (default), the address will be automatically 
//  converted to skip the holes at the end of each 1KB page
uint8_t lgt_eeprom_read_byte( uint16_t address, bool real_address_mode = false );
void lgt_eeprom_write_byte( uint16_t address, uint8_t value, bool real_address_mode = false );

// Read and write a buffer :
// - if the last parameter is false (default), the address will be automatically 
//  converted to skip the holes at the end of each 1KB page
void lgt_eeprom_read_block( uint8_t *pbuf, uint16_t address, uint8_t len, bool real_address_mode = false );
void lgt_eeprom_write_block( uint8_t *pbuf, uint16_t address, uint8_t len, bool real_address_mode = false );

// Read and write native 32bits data :
// - on the 328p, at the hardware level, the Flash/EEPROM is organized in 32bits cells (which contains 4 bytes).
//   It is, thus, more optimized to read/write from/to the EEPROM as 32bits aligned data.
// - /!\ These 2 specific functions do not skip the holes at the end of each 1KB page !
// - /!\ These 2 specific functions are emulated on 88a, 88d and 328d variants.
uint32_t lgt_eeprom_read32( uint16_t address );
void lgt_eeprom_write32( uint16_t address, uint32_t value );

// Read and write native 32bits buffer :
// - at the hardware level, the Flash/EEPROM is organized in 32bits cells (which contains 4 bytes).
//   It is, thus, more optimized to read/write from/to the EEPROM as 32bits aligned data.
// - "SWM" stands for "Serial Write Mode". It prevents flipping pages between each write, which is 
//   the most optimized way to write a buffer into the emulated EEPROM.
// - /!\ These 2 specific functions do not skip the holes at the end of each 1KB page !
// - /!\ These 2 specific functions are emulated on 88a, 88d and 328d variants.
void lgt_eeprom_writeSWM( uint16_t address, uint32_t *pData, uint8_t length );
void lgt_eeprom_readSWM( uint16_t address, uint32_t *pData, uint8_t length );

// MACRO : enable and disable the "SWM" mode :
// - they are used internally by lgt_eeprom_writeSWM().
// - /!\ These 2 specific functions are emulated on 88a, 88d and 328d variants.
lgt_eeprom_SWM_ON();
lgt_eeprom_SWM_OFF();

7.2) The improved LGT EEPROM object API

The "improved" LGT EEPROM object API is compatible with the original LGT EEPROM object API.

Because it can't be used at the same time than the Arduino EEPROM object API, it is disabled by default.

If you want to use this LGT EEPROM object API instead of the Arduino EEPROM object API, you'll have to #define USE_LGT_EEPROM_API before all #include <EEPROM.h>.

The differences with the original LGT EEPROM object API are :

  • it is not limited to 512 KB ;
  • by default, 8 bits read/write methods will automatically skip the reserved last 4 bytes of each 1KB pages, thus emulating a continuous space EEPROM (see §4 ) ;
  • read/write above E2END will not loop back to 0 (no overrun to 0) ;
  • 32bits and SWM read/write methods are now available to all LGT8Fx variants on which they will be emulated using simple and less optimized 8 bits read/write (on 88a, 88d and 328d).

The differences with the Arduino EEPROM object API are :

  • lower level and lighter API which mainly encapsulate the LGT EEPROM C/C++ API.
  • provides access to 32bits and SWM read/write methods optimized for the 328p.

Documentation :

// Returns the size (in bytes) of the emulated EEPROM :
// - theoretical = true : returns the theoretical size of the EEPROM (ignoring holes at the end of each 1KB page)
// - theoretical = false (default) : returns the number of bytes actually available for read/write (skipping holes at the end of each 1KB page)
int EEPROM.size( theoretical = false );

// Change the size of the EEPROM at run-time :
// - number_of_1KB_pages : 0 = disable EEPROM ;
// - number_of_1KB_pages : 1 (default) = emulated EEPROM size set to 1KB (use 2KB of Flash );
// - number_of_1KB_pages : 2 = emulated EEPROM size set to 2KB (use 4KB of Flash );
// - number_of_1KB_pages : 4 = emulated EEPROM size set to 4KB (use 8KB of Flash );
// - number_of_1KB_pages : 8 = emulated EEPROM size set to 8KB (use 16KB of Flash );
// /!\ above 1KB, the emulated EEPROM will overlap with the default BootLoader.
// /!\ above 2KB, the emulated EEPROM might overlap with the program.
// /!\ this method has no effect on 88a and 88d MCU variants.
// Returns the new size of the emulated EEPROM.
uint16_t EEPROM.change_size( number_of_1KB_pages );

// Read and write 8 bits value from/to the emulated EEPROM :
// - real_address_mode = true : holes at the end of each 1KB page will not be automatically skipped.
// - real_address_mode = false (default) : holes at the end of each 1KB page will be automatically skipped.
byte EEPROM.read( address, real_address_mode = false );
void EEPROM.write( address, byte value, real_address_mode = false );

// Read and write 8bits buffer :
// - real_address_mode = true : holes at the end of each 1KB page will not be automatically skipped.
// - real_address_mode = false (default) : holes at the end of each 1KB page will be automatically skipped.
void EEPROM.read_block( byte *buffer, address, len, real_address_mode = false );
void EEPROM.write_block( byte *buffer, address, len, real_address_mode = false );

// Read and write 32 bits value :
// - more optimized on 328p.
// /!\ holes at the end of each 1KB page will not be automatically skipped.
// /!\ emulated on 88a, 88d and 328d variants.
uint32_t EEPROM.read( address );
void EEPROM.write( address, uint32_t value );

// Read and write 32 bits buffer :
// - very optimized and fast on 328p.
// /!\ holes at the end of each 1KB page will not be automatically skipped.
// /!\ emulated on 88a, 88d and 328d variants.
void EEPROM.readSWM( address, uint32_t *buffer, len );
void EEPROM.writeSWM( address, uint32_t *buffer, len );

7.3) The Arduino EEPROM object API

This Arduino EEPROM object API implementation is mainly a copy/paste of the standard Arduino EEPROM object API implementation from the ATmega328p, glued to the LGT C/C++ API.

It is enabled by default at #include <EEPROM.h>, and can be used at the same time than the LGT EEPROM C/C++ API.

As it cannot be used at the same time than the LGT EEPROM object API ( see §7.2 ), optimized methods to 32 bits and SWM read/write will not be available.

For the documentation you can refer yourself to the default Arduino EEPROM documentation.

The only difference is :

// Change the size of the EEPROM at run-time :
// - number_of_1KB_pages : 0 = disable EEPROM ;
// - number_of_1KB_pages : 1 (default) = emulated EEPROM size set to 1KB (use 2KB of Flash );
// - number_of_1KB_pages : 2 = emulated EEPROM size set to 2KB (use 4KB of Flash );
// - number_of_1KB_pages : 4 = emulated EEPROM size set to 4KB (use 8KB of Flash );
// - number_of_1KB_pages : 8 = emulated EEPROM size set to 8KB (use 16KB of Flash );
// /!\ above 1KB, the emulated EEPROM will overlap with the default BootLoader.
// /!\ above 2KB, the emulated EEPROM might overlap with the program.
// /!\ this method has no effect on 88a and 88d MCU variants.
// Returns the new size of the emulated EEPROM.
uint16_t EEPROM.change_size( number_of_1KB_pages );

On 328p and 328d, the macro E2END is defined to (lgt_eeprom_size()-1).

8) LGT8Fx8-A and LGT8Fx8-D support

As mentioned previously, support for 88a, 88d and 328d have been added, but not tested.

LGT8Fx support EEPROM theoretical size EEPROM available space Flash cells 32bits r/w SWM r/w
88A untested 504 504 8 bits emulated emulated
48D no 1KB ? 16 bits - -
88D untested 2KB ? 16 bits emulated emulated
168D no 4KB ? 16 bits - -
328D untested 0/1K/2K/4K/8K 0/1020/2040/4080/8160 16 bits emulated emulated