cancel
Showing results for 
Search instead for 
Did you mean: 

EEPROM Emulation with Flash STM32F1

B C
Associate II
Posted on June 28, 2018 at 10:47

Hello everyone, 

I am currently working on a project on a STM32F103RBT6 and I want to save my different values to be able to still have them if my board reboots. Normally I would use the EEPROM of the �C, but it appears that there is no EEPROM on the STM32F1. However, it is said in some user manual that I could emulate the EEPROM function with the flash. 

The issue is that I don't understand how it's working. I've read the HAL Usermanual (as I am using the HAL API) to try to understand how to use the flash functions but it is not as documented as I was expecting.

Could you please help me with it ? How am I supposed to emulate EEPROM thanks to the flash ?

Thank you in advance.

I am using SW4STM32 with CUBEMX.

#stm32f1-flash #eeprom #eeprom-emulation-example
14 REPLIES 14
B C
Associate II
Posted on July 02, 2018 at 08:44

Up, anyone ?

B C
Associate II
Posted on July 03, 2018 at 09:39

Hello again everyone,

While searching a solution for emulating the EEPROM through Flash, I found this explanation :

https://stackoverflow.com/questions/49220921/stm32f4-discovery-writing-reading-flash-memory?noredirect=1&lq=1

and

https://stackoverflow.com/questions/28503808/allocating-memory-in-flash-for-user-data-stm32f4-hal

I think I understand most of it, but I am having trouble trying to change it so that it would suit my STM32F103RB specifications (as the code was written for a STM32F4).

Here is the corresponding code :

__attribute__((__section__('.user_data'))) const char userConfig[64];
//data type have to be consistent with the TYPEPROGRAM, i.e:
//TYPEPROGRAM_BYTE uint8_t data//TYPEPROGRAM_HALFWORD uint16_t data
//TYPEPROGRAM_WORD uint32_t data//TYPEPROGRAM_DOUBLEWORD uint64_t data
void Write_Flash(uint32_t data[],uint8_t flashTypeProgram)
{ 
uint8_t addressGap; 
HAL_FLASH_Unlock();
 __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
 FLASH_Erase_Sector(FLASH_SECTOR_6, VOLTAGE_RANGE_3);
 for (i=0;i<64/pow(2, flashTypeProgram);i++)
 {
 addressGap=pow(2, flashTypeProgram)*i;
 HAL_FLASH_Program(flashTypeProgram, &userConfig[0]+addressGap, data[i]);
 }
 HAL_FLASH_Lock();
 //TYPEPROGRAM_BYTE Program byte (8-bit) at a specified address $0
 //TYPEPROGRAM_HALFWORD Program a half-word (16-bit) at a specified address $1
 //TYPEPROGRAM_WORD Program a word (32-bit) at a specified address $2
 //TYPEPROGRAM_DOUBLEWORD Program a double word (64-bit) at a specified address $3}
[...]
flashTypeProgram=TYPEPROGRAM_WORD;
dataSize=(sizeof dataBuffer) / (sizeof *dataBuffer);
for (i=0;i<dataSize;i++)
{
 dataBuffer[i]=0x1010101; //0x1010101 puts 1 in each byte of userConfig
}
Write_Flash(dataBuffer,flashTypeProgram);

and in the .ld file there is :

MEMORY
 {
 FLASH (RX) : ORIGIN = 0x08000000, LENGTH = 1M-128K
 DATA (RWX) : ORIGIN = 0x080E0000, LENGTH = 128k
 ...
 ...
 }

[...]

.user_data : { . = ALIGN(4); *(.user_data) . = ALIGN(4); } > DATA

As I don't want to brick my card messing around with flash memory assignement, I want to make sure to understand fully what's happening and how I could translate it with my STM32F1RB (medium density) specifications.

First of all, I should allow the Data zone, starting from the end of the main memory zone, right ? For example I could assign pages 127 - 107 for Data purposes ?

MEMORY
 {
DATA (RWX): ORIGIN = 0x08048000, LENGTH = 20k/* 0x08048000 is the beginning of page 107 */
...

}

that would be good to allow 20 pages for flash storage ?

Next, I don't exactly understand this line :

__attribute__((__section__('.user_data'))) const char userConfig[64];

I get that it's for the user to be able to read the Flash stored data, but why is it assigned this way ? I mean, 64*sizeof(char) = 64 bytes, right ? How is he assigning only 64 bytes for reading purposes while he allowed 128 kB of storage ?

I still have some questions, but if you could already help me figuring out these things you would help me a lot !

In advance, thanks !

Andreas Bolsch
Lead II
Posted on July 03, 2018 at 12:38

Maybe you should have a look at

https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32010.html

 

and in particular at the corresponing application note.

Posted on July 03, 2018 at 11:47

Hi Bastien,

what you digged up from the web is a really good starting point and I cheer your approach to fully understand what's happening before adapting the code.

First of all, I should allow the Data zone, starting from the end of the main memory zone, right ? For example I could assign pages 127 - 107 for Data purposes ?

Jep, generally spoken it's a good idea to start your data zone from the end of the main memory as the starting point of the application, i.e.

FLASH (RX) : ORIGIN = 0x08000000

is at the beginning of of the main memory. And to keep as much space for the application as possible it's smart to put the data zone at the end.

that would be good to allow 20 pages for flash storage ?

Pages 107 through 127 actually are 21 pages.

You shall dimension the number of pages you use for data storage depending on your expected application size and your needed data storage. How much space is available on one page is stated in the data-sheet and differs broadly among the chips of the STM32 family (for STM32F4 the pages/sectors at the end of the memory have a size of 128K).

Make sure that you also adapt this line correctly as well:

 FLASH (RX) : ORIGIN = 0x08000000, LENGTH = 1M-128K

In total the different areas defined in the MEMORY { } tag of the linker script must not exceed (better: do exactly match) the total available space (depending on your chip - for STM32F4 it's 1M).

Now let's regard the specific line in question:

__attribute__((__section__('.user_data'))) const char userConfig[64];

with the __attribute(...) you tell the linker to put the 64 element array 'userConfig' of type char not in the normal memory area, but the special one defined in the linker script.

(Side note: If you have even other special areas of the memory map, e.g. core coupled RAM, you can use the very same technique to place variables there).

The STM32's memory interface makes it really easy to read from the user data as you have only access this specially placed constant variable 'userConfig' as you would access any other variable.

Now let's turn to your question why 128K are reserved but actually only 64*sizeof(char) are used.

First of all: your understanding of the code is perfectly correct. 128K in fact are reserved but only 64*sizeof(char) of it used.

But why?

The reason why 128K are reserved is caused by the structure and technology of flash memory. Generally spoken: Before you can rewrite a flash page or sector you have to erase it completly, even if you only want to change a single bit. (this is done in the Write_Flash method of your example before the for-loop). Not to corrupt accidentially any data, e.g. of your application when updating the user config or vice versa your config when installing a new firmware version, it is essential to define the memory areas in the linker script aligned to the pages/sectors of the flash memory! And as said before on STM32F4 a sector (at the end of the flash) is 128K, thus it's the smallest amount of data (if any) to reserve for user data. On the other hand, you shall only access as much data as you actually need (as writing the flash consumes time etc.). So I guess, the example code is using only 64*sizeof(char) for the sake of simplicity (as one can demonstrate the different TYPEPROGRAM options and has a siginifcant amount of data one has to handle in the for-loop).

Actually I would refactor this part of the example to set the size of the array (and consequently the iterations of the for-loop) with a preprocessor define. So that if at a later stage of implementation, I come up with some more values to be stored in the user config makes code maintanance as easy as possible.

Another idea, in case you have more complex data to store, is to define a struct and place an instance at the .user_data area (instead of the char-array). But that's things you can think about when you are familiar with the basics ;-) Cheers, Thomas
Posted on July 03, 2018 at 14:57

Hi Thomas, first of all, thank you a lot for your help !

Pages 107 through 127 actually are 21 pages.

You shall dimension the number of pages you use for data storage depending on your expected application size and your needed data storage. How much space is available on one page is stated in the data-sheet and differs broadly among the chips of the STM32 family (for STM32F4 the pages/sectors at the end of the memory have a size of 128K).

Yes, I didn't count thoroughfully but obviously 107 to 127 is 21 pages ...

For my application I am planning on storing 4 kind of structures, there can be up to 50 of each of the 4 structures. The first one is made of 7 words (uint32_t), the second one 3 words and 1 byte (so I rounded it up to 4 words), the third one 9 words and the last one 4 words. if I have 1 structure of each it takes 24 words.

1 word is 4 bytes, so 1 structure of each takes 90 bytes.

On the STM32F1 medium size, 1 page is 1kB :

https://www.st.com/content/ccc/resource/technical/document/programming_manual/10/98/e8/d4/2b/51/4b/f5/CD002834pdf/files/CD002834pdf/jcr:content/translations/en.CD002834pdf

page 7 since STM32F1RBT6 is a medium density device.

hence I can put 1024/90 = 37 ~ 11 structures of each per page.

Worst case scenario, I have to store 50 structures of each, so I should take 5 pages (I just realize that my previous calculations were WAY off ...).

Does it seems correct to you ? If not, could you explain why ?

Make sure that you also adapt this line correctly as well:

 FLASH (RX) : ORIGIN = 0x08000000, LENGTH = 1M-128K

In total the different areas defined in the MEMORY { } tag of the linker script must not exceed (better: do exactly match) the total available space (depending on your chip - for STM32F4 it's 1M).

My linker script is looking like that at the moment :

MEMORY

{ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K }

I should correct it to this :

MEMORY

{ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K - 5K DATA (rwx) : ORIGIN = /* Adress of page 123 */, LENGTH = 5K /* Because 5 pages */ }

am I right ? (without the comments and with the corresponding adress obviously).

Now let's regard the specific line in question:

__attribute__((__section__('.user_data'))) const char userConfig[64];

with the __attribute(...) you tell the linker to put the 64 element array 'userConfig' of type char not in the normal memory area, but the special one defined in the linker script.

(Side note: If you have even other special areas of the memory map, e.g. core coupled RAM, you can use the very same technique to place variables there).

The STM32's memory interface makes it really easy to read from the user data as you have only access this specially placed constant variable 'userConfig' as you would access any other variable.

Now let's turn to your question why 128K are reserved but actually only 64*sizeof(char) are used.

First of all: your understanding of the code is perfectly correct. 128K in fact are reserved but only 64*sizeof(char) of it used.

But why?

The reason why 128K are reserved is caused by the structure and technology of flash memory. Generally spoken: Before you can rewrite a flash page or sector you have to erase it completly, even if you only want to change a single bit. (this is done in the Write_Flash method of your example before the for-loop). Not to corrupt accidentially any data, e.g. of your application when updating the user config or vice versa your config when installing a new firmware version, it is essential to define the memory areas in the linker script aligned to the pages/sectors of the flash memory! And as said before on STM32F4 a sector (at the end of the flash) is 128K, thus it's the smallest amount of data (if any) to reserve for user data. On the other hand, you shall only access as much data as you actually need (as writing the flash consumes time etc.). So I guess, the example code is using only 64*sizeof(char) for the sake of simplicity (as one can demonstrate the different TYPEPROGRAM options and has a siginifcant amount of data one has to handle in the for-loop).

Ok so if I still understand fully, with that loop he created :

for (i=0;i<dataSize;i++)
{
 dataBuffer[i]=0x1010101; //0x1010101 puts 1 in each byte of userConfig
}
Write_Flash(dataBuffer,flashTypeProgram);

if he then looks atuserConfig[i] (whatever i is), he will have 0x01, right ?

But he could've wrote :

__attribute__((__section__('.user_data'))) const uint32_t userConfig[16];

to allocate the same amount of data, right ? (the access would have been different (userConfig[i] would be equal to 0x01010101 instead of 0x01) but the amount of data the same)

Actually I would refactor this part of the example to set the size of the array (and consequently the iterations of the for-loop) with a preprocessor define. So that if at a later stage of implementation, I come up with some more values to be stored in the user config makes code maintanance as easy as possible.

I am not used to doing this yet, but I'll try to do it or at least take it into account

Another idea, in case you have more complex data to store, is to define a struct and place an instance at the .user_data area (instead of the char-array). But that's things you can think about when you are familiar with the basics 😉

That's where it gets interesting. To sum it up, I have 4 'main' kind of data to store. One structure 'Tab_TxCAN' which size is 7 words, one 'Tab_Operation' structure which size is 3 words and 1 byte (rounded to 4 words for the previous calculations), one 'Tab_RxCAN' which size is 9 words, and one 'Tab_ADC' which size is 4 words.

In my program, I dynamically allocate these structures to create a table of structures, depending on 3 other variables : nb_CAN_Tx_Frames, nb_CAN_Rx_Frames, nb_CAN_ADC_Frames (size of Tab_Operation table and Tab_RxCAN table is the same). (As each nb_... is saved on a uint32_t, I had in mind to 'concatenate' them to be able to store these 3 variables in 1 word only (as each of them shouldn't go above 50 (0x32), that way I could use less space to store everything).

I don't think it would be possible to do it the same way as it is done in my main.c file

i.e :

__attribute__((__section__('.user_data'))) const uint32_t nb_CAN_Tx_Frames;

__attribute__((__section__('.user_data'))) const Tab_TxCAN_Struct *Tab_TxCAN = malloc(nb_CAN_Tx_Frames * sizeof(Tab_TxCAN));

or is it ?

If I had to do it by myself I would allocate the maximum for each structure (i.e : 50 of each) and then fill in just the necessary, but I gues it wouldn't be very wise (as it would make my flash wear faster and take a lot of time to write here).

What would you do in that case ?

Last but not least, a question just came to mind, is it common to save in the flash (or EEPROM when you have one) a number representing the number of time each page had been erased and rewrote ? That way you could change the adress when this number is coming close to the maximum expected number of cycles.

Thanks again for your help !

Bastien

Posted on July 03, 2018 at 15:00

I read this application note many times but the functions are from another API than the one I am using. The main problem I am having is accessing the data I stored in the flash (as there is no function for this in the HAL library).

However, this application note helped me a lot understanding the 'page' system.

Thank you for your help

Posted on July 03, 2018 at 16:52

B C wrote:

I read this application note many times but the functions are from another API than the one I am using. The main problem I am having is accessing the data I stored in the flash (as there is no function for this in the HAL library).

There is no need for any function to access (i. e. read) the flash. Just compute the address and access it with the '*' operator, e. g.

uint8_t *address = 0x08000000;

then you can simply say something like

*address + 5;

to read the byte at 0x08000000 and add 5 to that value. The only interesting part is to calculate the address. For this you need to find out where the most recent version of your data had been written to.

Posted on July 03, 2018 at 19:23

Does it seems correct to you ? If not, could you explain why ?

Well 24words * 4Bytes/word = 96Bytes. But the final result shall not be affected. But make sure, that your compiler 'counts' the same way as you. You can verify this by printing the result of the sizeof() command.

MEMORY

{

RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K - 5K DATA (rw x) : ORIGIN = /* Adress of page 123 */, LENGTH = 5K /* Because 5 pages */ }

am I right ? (without the comments and with the corresponding adress obviously).

Looks good. If you are pedantic you shall eliminate the 'x' for DATA as it marks the area as containing eXecutable code (which is not the case).

Ok so if I still understand fully, with that loop he created :

for (i=0;i<dataSize;i++)
{
 dataBuffer[i]=0x1010101; //0x1010101 puts 1 in each byte of userConfig
}
Write_Flash(dataBuffer,flashTypeProgram);

if he then looks atuserConfig[i] (whatever i is), he will have 0x01, right ?

But he could've wrote :

__attribute__((__section__('.user_data'))) const uint32_t userConfig[16];

to allocate the same amount of data, right ? (the access would have been different (userConfig[i] would be equal to 0x01010101 instead of 0x01) but the amount of data the same)

jep you got that right.

I don't think it would be possible to do it the same way as it is done in my main.c file

i.e :

__attribute__((__section__('.user_data'))) const uint32_t nb_CAN_Tx_Frames;

__attribute__((__section__('.user_data'))) const Tab_TxCAN_Struct *Tab_TxCAN = malloc(nb_CAN_Tx_Frames * sizeof(Tab_TxCAN));

or is it ?

That's exactly what I aimed at. Of course, thus the writing logic of gets a little more complicated

Write_Flash

()

If I had to do it by myself I would allocate the maximum for each structure (i.e : 50 of each) and then fill in just the necessary, but I gues it wouldn't be very wise (as it would make my flash wear faster and take a lot of time to write here).

What would you do in that case ?

Yeah, I would also allocate the maximum possible but then let the Write_Flash() method decide how many entries of the array are actually filled and write only those.

Regarding the flash wearing out due to many write/erase cycles, I unfortunately have no proper experience, as in my applications (and product lifetime) I do not have to worry about that.

What you suggested sounds like a good starting point to me, but further research will most probably reveal several strategies to face the issue 😉

Best regards,

Thomas
Posted on July 04, 2018 at 09:19

uint8_t *address = 0x08000000;

then you can simply say something like

*address + 5;

to read the byte at 0x08000000 and add 5 to that value. The only interesting part is to calculate the address

I feel a bit dumb for not having thought about it earlier ...

But now that I have an alternate solution (see the replies above), what would you recommend doing ? Are there any differences between these two ways of doing ?

What are the advantages and drawbacks ?