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
Posted on July 04, 2018 at 11:41

Hello Thomas,

Thank you again for your help, but I still have a question.

When we adress the elements to be stored in the special memory area defined in the linker script, i.e :

__attribute__((__section__('.user_data'))) const uint32_t nb_CAN_Tx_Frames; 
__attribute__((__section__('.user_data'))) const uint32_t nb_Erase;
__attribute__((__section__('.user_data'))) const Tab_TxCAN_Struct Tab_TxCAN [4];�?�?�?�?�?�?�?�?�?

is it possible to specify the adress I want these elements to be stored ?

The issue I am having is that as I am using structures of various sizes (7 words, 9 words...), I don't always manage to store all of them in a single memory page, and I can't always fill a memory page with an integer number of structures.

That way, I am leaving some blank space at the end of some pages, it looks like this :

(a memory page is on 1 kB, which is 256 words (4 B per word).)

memory page 1 :

[...]

Tab_TxCAN[2] (words 247 to 253)

blank space (words 254 to 255)

beginning of memory page 2

Tab_TxCAN[3] (words 0 to 6)

I would want in that case to specify in the adressing that Tab_TxCAN[3] would start at adress number : 'beginning of page 2'.

If I am not clear, please let me know, I'll try to rephrase it !

Thank you again in advance !

Posted on July 04, 2018 at 17:59

Hey again,

I don't think that you can control where the linker puts your data in the defined section, although I've never wondered about it.

Another issue is that an array is by definition a continuous portion of memory and that array[i] for an array of type T is basically only an alias of *(array + i*sizeof(T)).

So I don't think, that you can put your different instances of the struct as you would like.

Actually I also do not see the point why you would like to do it.

Not to erase and rewrite sectors which actually have not been changed? You could achieve that with your writing logic no matter where the different arrays and their elements are stored.

Another idea to reduce the read/write cycles would be to define different sections in the linker script for your 4 arrays of different types of structs.

Regarding your question whether to access the flash using pointers only (as Andreas explained/suggested) or via the variables located by the linker in special areas, for your application I'd stick to the variables.

There are two main reasons:

Firstly, to safely store your user config, you have to adapt the linkerscript anyways. If you don't it might happen that the linker will place something different (i.e. executable code) onto the sectors you intend to use for your user config - a total mess would be the same.

Secondly, on assembly level both options are basically the same. So I don't expect the one or the other option to be significantly faster or produce smaller executables or whatever. It remains the 'usability' for the programmer: And regarding this the option of variables located in special areas is (in my opinion) much better, especially as you handle complex structs. When working with the bare pointers you always have to make sure that you point to the data you would like to access, which can be pain in the a****. Just image you want to access the second word of the fourth element of your third type of struct.

Using plain pointers that would be something like:

const ThirdStruct* thirdArrayBase = 0x8000AAAA;

uint32_t desiredValue = *(((uint32_t*) (thirdArrayBase + 4)) + 2);

Using the variables you could use 'speaking' names which is commonly much less error prone:

uint32_t desiredValue = thirdArray[4].secondWord;

Best regards,

Thomas
Posted on July 05, 2018 at 09:28

Hello !

Actually I also do not see the point why you would like to do it.

Not to erase and rewrite sectors which actually have not been changed? You could achieve that with your writing logic no matter where the different arrays and their elements are stored.

Not exactly, as explained in my previous message, 256 is not a multiple of 7 or 9 (one structure is 7 words big, one is 9 words big). Because of this, I can't put the memory zone of 50 of these structures on only one page and if I just put the 50 structures in one 'block', one structure will have some words on one page and some on the other. 

beginning of memory page 1

[...]

Tab_TxCAN[2] (words 247 to 253)

Tab_TxCAN[3] (first 2 words : words 254 to 255)

beginning of memory page 2 

Tab_TxCAN[3] (last 5 words : words 0 to 4)

As I want my code to be as 'generic' as possible, I wanted to avoid to deal with special cases (because with Tab_TxCAN the junction between two pages is between word 2 and 3, but with another frame it is between words 5 and 6 for example).

On a side note, when I write, I erase page 1, then write on page 1, then erase page 2, then write on page 2 .... until page 5. If I erase all the pages and then write everything at once, would it cause a problem ?

Another idea to reduce the read/write cycles would be to define different sections in the linker script for your 4 arrays of different types of structs.

Could you elaborate on this, how can it reduce the read/write cycles ? 

Regarding your question whether to access the flash using pointers only (as Andreas explained/suggested) or via the variables located by the linker in special areas, for your application I'd stick to the variables.

There are two main reasons:

Firstly, to safely store your user config, you have to adapt the linkerscript anyways. If you don't it might happen that the linker will place something different (i.e. executable code) onto the sectors you intend to use for your user config - a total mess would be the same.

Secondly, on assembly level both options are basically the same. So I don't expect the one or the other option to be significantly faster or produce smaller executables or whatever. It remains the 'usability' for the programmer: And regarding this the option of variables located in special areas is (in my opinion) much better, especially as you handle complex structs. When working with the bare pointers you always have to make sure that you point to the data you would like to access, which can be pain in the a****. Just image you want to access the second word of the fourth element of your third type of struct.

Using plain pointers that would be something like:

const ThirdStruct* thirdArrayBase = 0x8000AAAA;

uint32_t desiredValue = *(((uint32_t*) (thirdArrayBase + 4)) + 2);

Using the variables you could use 'speaking' names which is commonly much less error prone:

uint32_t desiredValue = thirdArray[4].secondWord;

That's what I thought, but I wanted to make sure

Thank you again, and sorry if some questions may seem a bit dumb, I am still very new to flash/eeprom programming and don't want to mess everything up !

Posted on July 12, 2018 at 09:46

Hello,

So I finally started coding, but for some reason it is not working ... Whenever I am programming something, it wil return an error :HAL_FLASH_ERROR_PROG which is a programming error. When I look at it in the flash status register, it means :Set by hardware when an address to be programmed contains a value different from '0xFFFF' before programming.

When I then parse into the FLASH memory of my STM32F1, I see that memory rangin from :0x801EC00 to0x801F17F is filled with 0x0000. Hence the Programming error.

What I don't understand is why this part of the memory hasn't been erased. Here is my code :

/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K - 5K
Erases (RW): ORIGIN = 0x801EC00, LENGTH = 4
Frames (RW): ORIGIN = 0x801EC04, LENGTH = 4
TRAMESTx (RW): ORIGIN = 0x801EC08, LENGTH = 1400
TRAMESRx (RW): ORIGIN = 0x801F180, LENGTH = 1800
TRAMESADC(RW): ORIGIN = 0x801F888, LENGTH = 800
TRAMESOP (RW): ORIGIN = 0x801FBA8, LENGTH = 800
}

/* Define output sections */
SECTIONS
{
 /* The startup code goes first into FLASH */
 .isr_vector :
 {
 . = ALIGN(4);
 KEEP(*(.isr_vector)) /* Startup code */
 . = ALIGN(4);
 } >FLASH
 
 .nb_Erases : 
 {
 . = ALIGN(4);
 
 *(.nb_Erases)
 
 . = ALIGN(4);
 } > Erases
 .nb_Frames : 
 {
 . = ALIGN(4);
 
 *(.nb_Frames)
 
 . = ALIGN(4);
 } > Frames
 .user_TramesTx : 
 {
 . = ALIGN(4);
 
 *(.user_TramesTx)
 
 . = ALIGN(4);
 } > TRAMESTx
 .user_TramesRx : 
 {
 . = ALIGN(4);
 
 *(.user_TramesRx)
 
 . = ALIGN(4);
 } > TRAMESRx
 .user_TramesADC : 
 {
 . = ALIGN(4);
 
 *(.user_TramesADC)
 
 . = ALIGN(4);
 } > TRAMESADC
 .user_TramesOp : 
 {
 . = ALIGN(4);
 
 *(.user_TramesOp)
 
 . = ALIGN(4);
 } > TRAMESOP�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

for the linker script

#include 'includes.h'
__attribute__((__section__('.nb_Erases'))) const uint32_t nombre_Effacement_Flash;
__attribute__((__section__('.nb_Frames'))) const uint32_t nombre_Trames_Flash;
__attribute__((__section__('.user_TramesTx'))) const CanTxMsgTypeDef CAN_Tab_TxMessages_Flash[50];
__attribute__((__section__('.user_TramesRx'))) const CanRxMsgTypeDef CAN_Tab_RxMessages_Flash[50];
__attribute__((__section__('.user_TramesADC'))) const ADC_TxTypeDef CAN_ADC_Tab_TxMessages_Flash[50];
__attribute__((__section__('.user_TramesOp'))) const CAN_operation Operation_Flash[50];

int EcriturePreparation_Flash(void)
/* Resumé : Fonction qui prépare à l'écriture dans la Flash. Elle débloque la Flash et clear les flags.
 *
 * Arguments : page_number, page_adress
 *
 * Retourne : -1 si tout a bien marché, le numéro de la page avec l'erreur sinon
 *
 */
{
FLASH_EraseInitTypeDef *Efface_Page = NULL;
uint32_t *Erreur_Effacement = NULL;

if (HAL_FLASH_Unlock() != HAL_OK)// On unlock la flash pour pouvoir écrire dedans
{
return(0);
}
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR | FLASH_FLAG_BSY | FLASH_FLAG_OPTVERR );// Clear de tous les flags de FLASH
Efface_Page = malloc(sizeof(Efface_Page));// On configure la manière d'effacement des pages de la FLASH
Erreur_Effacement = malloc(sizeof(Erreur_Effacement));
if(Efface_Page == NULL || Erreur_Effacement == NULL)
{
Error_Handler();
}
Efface_Page->TypeErase = FLASH_TYPEERASE_PAGES;
Efface_Page->PageAddress =0x801EC00; //ADDRESSE DU DEBUT DE DATA SAUVEGARDE
Efface_Page->NbPages = 5;
HAL_FLASHEx_Erase(Efface_Page, Erreur_Effacement);// On efface la FLASH (obligatoire pour écrire)
if(*Erreur_Effacement != 0xFFFFFFFF)
{
return(*Erreur_Effacement);// Si il y a une erreur, Erreur_Effacement prend la valeur de la page erronée
}
else
{
return(-1);// -1 = tout s'est bien passé (valeur négative pour être sûr que ça ne soit pas un numéro de page)
}
}

// DATA (RW): ORIGIN = 0x801EC00, LENGTH = 5K
/* A CHANGER DANS LE LINKER FILE SI CA NE MARCHE PAS COMME FAIT */



int EcritureTrame_Flash_CANTx(CanTxMsgTypeDef *CAN_Tab_TxMessages)
/* Résumé : Fonction qui écrit les trames CANTx en mémoire Flash
 *
 * Arguments : Adresse_Tab_Trame_Flash, Tab_Trame, compteur_nbtrames_debut, compteur_nbtrames_max, nb_trames, nb_word
 *
 * Retourne : 1 si tout a bien marché, 0 sinon
 *
 */
{
uint32_t nombre_Effacement = 0;
uint32_t nombre_trames_Tx = 0;
//const CanTxMsgTypeDef CAN_Tab_TxMessages_Flash[50];
//const uint32_t nombre_Effacement_Flash;
int cpt_trames;
nombre_Effacement = nombre_Effacement_Flash + 1;
nombre_trames_Tx = CAN_nb_trames_Tx_cc;
for (cpt_trames = 0 ; cpt_trames < nombre_trames_Tx ; cpt_trames ++)
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].DLC, CAN_Tab_TxMessages[cpt_trames].DLC) != HAL_OK)// On programme chaque paramètre de la structure Tab_TxMessages un par un
{// Les adresses où on écrit dans la Flash sont passées en (intptr_t) car ils veulent un entier en argument et pas un pointeur
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].ExtId, CAN_Tab_TxMessages[cpt_trames].ExtId) != HAL_OK)
{
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].IDE, CAN_Tab_TxMessages[cpt_trames].IDE) != HAL_OK)
{
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].RTR, CAN_Tab_TxMessages[cpt_trames].RTR) != HAL_OK)
{
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].StdId, CAN_Tab_TxMessages[cpt_trames].StdId) != HAL_OK)
{
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].Data[0], CAN_Tab_TxMessages[cpt_trames].Data[0]) != HAL_OK)
{
return 0;
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].Data[4], CAN_Tab_TxMessages[cpt_trames].Data[4]) != HAL_OK)
{
return 0;
}
}
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&nombre_Effacement_Flash, nombre_Effacement) != HAL_OK)// On écrit le nombre d'effacements effectués depuis le début d'utilisation de la Flash. (NOTE X)
{
return 0;
}
return 1;


}

int Sauvegarde_Flash(CanTxMsgTypeDef *CAN_Tab_TxMessages)
/* Résumé : Fonction qui sauvegarde les configurations de MACS en mémoire Flash
 *
 * Arguments : Adresse_Tab_Trame_Flash, Tab_Trame, compteur_nbtrames_debut, compteur_nbtrames_max, nb_trames, nb_word
 *
 * Retourne : 1 si tout a bien marché, 0 sinon
 *
 */
{
int test = 0;
test = EcriturePreparation_Flash();// Préparation à l'écriture
if (test != -1)
{
Error_Handler();
}
if(EcritureTrame_Flash_CANTx(CAN_Tab_TxMessages) != 1)// On écrit
{
Error_Handler();
}
if(HAL_FLASH_Lock() != HAL_OK)// On verouille la FLASH (NOTE X)
{
Error_Handler();
}
return 1;

}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

The function 'Sauvegarde_Flash' is the one I call in main.c to save the CAN_Tab_TxMessages in the FLASH. EcriturePreparation_Flash() is supposed to prepare the Flash for being written (unlock, erase the 5 pages, clear the flags). Finally, EcritureTrame_Flash_CANTx writes every frame of the array in the FLASH.

If you have any idea on why HAL_FLASHEx_Erase() is not working ...

Just to add : there is no special warning or error when I build my code.

Thank you in advance !

B C
Associate II
Posted on July 16, 2018 at 15:56

So, I've tried to change the address of HAL_FLASH_Program :

int EcritureTrame_Flash_CANTx(CanTxMsgTypeDef *CAN_Tab_TxMessages)
/* Résumé : Fonction qui écrit les trames CANTx en mémoire Flash
 *
 * Arguments : Adresse_Tab_Trame_Flash, Tab_Trame, compteur_nbtrames_debut, compteur_nbtrames_max, nb_trames, nb_word
 *
 * Retourne : 1 si tout a bien marché, 0 sinon
 *
 */
{
 uint32_t nombre_Effacement = 0;
 uint32_t nombre_trames_Tx = 0;
// const CanTxMsgTypeDef CAN_Tab_TxMessages_Flash[50];
// const uint32_t nombre_Effacement_Flash;
 int cpt_trames;
 nombre_Effacement = nombre_Effacement_Flash + 1;
 nombre_trames_Tx = CAN_nb_trames_Tx_cc;
 for (cpt_trames = 0 ; cpt_trames < nombre_trames_Tx ; cpt_trames ++)
 {
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_TxMessages_Flash[cpt_trames].DLC, CAN_Tab_TxMessages[cpt_trames].DLC) != HAL_OK) // On programme chaque paramètre de la structure Tab_TxMessages un par un
 { // Les adresses où on écrit dans la Flash sont passées en (intptr_t) car ils veulent un entier en argument et pas un pointeur
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].ExtId, CAN_Tab_TxMessages[cpt_trames].ExtId) != HAL_OK)
 {
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].IDE, CAN_Tab_TxMessages[cpt_trames].IDE) != HAL_OK)
 {
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].RTR, CAN_Tab_TxMessages[cpt_trames].RTR) != HAL_OK)
 {
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].StdId, CAN_Tab_TxMessages[cpt_trames].StdId) != HAL_OK)
 {
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].Data[0], CAN_Tab_TxMessages[cpt_trames].Data[0]) != HAL_OK)
 {
 return 0;
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&CAN_Tab_RxMessages_Flash[cpt_trames].Data[4], CAN_Tab_TxMessages[cpt_trames].Data[4]) != HAL_OK)
 {
 return 0;
 }
 }
 if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&nombre_Effacement_Flash, nombre_Effacement) != HAL_OK) // On écrit le nombre d'effacements effectués depuis le début d'utilisation de la Flash. (NOTE X)
 {
 return 0;
 }
 return 1;


}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

&CAN_Tab_TxMessages_Flash became &CAN_Tab_RxMessages. Which means that I'm trying to write between adresses :

0x801F180 to

0x801F8 But now, this part became full of 00000000 and it's full of FFFFFFFF between 0x801EC04 and 0x801F180 ...

So it appears that depending on where I want to write, the address won't erase properly ... But I don't know how to deal with it ...

I join a picture of my memory browser, if this is helping in any way ...

0690X0000060QnjQAE.png 0690X0000060QnkQAE.png

The top picture is when I try to write in TxMessages (for some reason, some of the RxMessages is not erased...). The bottom one is when I try to write in Rxmessages. Nombre_Trames is always well erased because I never try to write here for now.

I am stuck, I've been trying to figure out why it's not working but I just can't...