Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
8 years, 6 months ago.
Is there a way to store variables in a non-volatile memory without EEPROM?
So I'm using the STM32F411 Nucleo-64. I'm making a system for my PC that controls the cooling system as well as other things. The problem I have is that I want to store things like the previously selected fan speed when the PC is powered down so that upon start up the fan is at the same speed. Normally I would use EEPROM but this board doesn't have any available, I was wondering if it was possible to use one of the memory blocks used for the program memory for data storage.
int Fanspeed __attribute__((at(0x08060000)));// store pwm values for power off
I was thinking of using something like this, the location shown above is in the final block of memory so shouldn't ever be used as my program is fairly small. I'm just not sure that I can simply write and read like this as if it was just a variable.
4 Answers
8 years, 4 months ago.
Don't know if you got it yet but I've just come across the same issue. Here is the code that emulates EEPROM using Flash for STM32F103
https://developer.mbed.org/users/olympux/code/eeprom_flash/
This code use HAL flash from mbed rather than AN2594 application note from ST, which you can have a look here:
https://developer.mbed.org/users/olympux/code/eeprom/
The latter one, based on AN2594, worked fine for me, but recently has broken with recent mbed updates. It's still fine with previous mbed revisions though.
8 years, 6 months ago.
You have to use the IAP options of your device. Since they didn't standardize it for STM32s it was a bit hard to write a library for it and I don't think one exists.
As example which can get you started (alternative is using the STM32 hal drivers), the .cpp file of a library I once started for this purpose:
#include "StmIAP.h" #ifdef TARGET_STM void unlock_flash(bool unlock); //(Un)lock flash memory IAPCode check_error(void); //Return errors uint8_t sector_number(uint32_t addr); //Return sector number of the given flash address IAPCode erase_sector(int address) { unlock_flash(true); //Clear current errors FLASH->SR = FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR; //Run command FLASH->CR = FLASH_CR_SER | (sector_number(address) << 3); FLASH->CR |= FLASH_CR_STRT; IAPCode retval = check_error(); unlock_flash(false); return retval; } IAPCode program_flash(int address, char *data, unsigned int length) { unlock_flash(true); FLASH->CR = FLASH_CR_PG; uint8_t* write_addr = (uint8_t*)address; for (int i = 0; i<length; i++) { while (FLASH->SR & FLASH_SR_BSY); write_addr[i] = data[i]; } IAPCode retval = check_error(); FLASH->CR = 0; unlock_flash(false); return retval; } uint8_t sector_number(uint32_t addr) { uint8_t retval = 0; while(1) { //If start address of next sector is higher than wanted address, return current value if (addr < flash_sectors[retval + 1]) return retval; retval++; } } IAPCode check_error(void) { //Wait until done while (FLASH->SR & FLASH_SR_BSY); //Check for errors if (FLASH->SR & FLASH_SR_WRPERR) return WriteProtError; if (FLASH->SR & FLASH_SR_PGAERR) return AllignmentError; if (FLASH->SR & FLASH_SR_PGPERR) return ParallelismError; if (FLASH->SR & FLASH_SR_PGSERR) return SequenceError; return Success; } uint32_t flash_size(void) { return *flash_size_addr * 1024; } uint32_t sector_size(uint32_t addr) { uint8_t sector = sector_number(addr); return flash_sectors[sector+1] - flash_sectors[sector]; } void unlock_flash(bool unlock) { if (unlock) { __disable_irq(); //Wait until not busy while (FLASH->SR & FLASH_SR_BSY); FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB; } else { FLASH->CR |= FLASH_CR_LOCK; __enable_irq(); } } #endif
Remember flash you can always read, writing needs something special (although on the STM32F4s it is quite nice made where you can write to the address the normal way after you gave some commands), but before writing you need to make sure it is erased, which goes per sector.
8 years, 6 months ago.
To EEPROM emulation in the STM32, where there is no hardware EEPROM, there is a very useful library from STM.
Using this library has many advantages, eg. increasing the lifetime of flash memory and higher speed by eliminating every time memory erase. And it is very easy to use simple test EEPROM emulation for STM32F401 (STM algorithm described in the application notes: AN4061, AN3969, AN2594, AN3390, AN4056).
The description is here:
https://my.st.com/content/my_st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries-expansions/stsw-stm32066.html
However, in this application note are the source based on the SPL.
But I tested a different version adapted to the library HAL (from CubeMX) and it works very well with mbed. I did a quick and simple test and there are also the source of this library:
Import program00_eeprom_emulation_f401
The only restriction is that the variables are only the size of two bytes (uint16_t), but you can use them to store the variables of a different size.
If necessary, in a file eeprom.h you need to redefine the address of free space in flash memory (EEPROM_START_ADDRESS) and the number of used variables (NB_OF_VAR).
Try this version. It is based on code from the STM, for your Nucleo F091. Unfortunately I do not have the possibility to test this code.
Import program00_eeprom_emulation_f091
simple test EEPROM emulation (STM algorithm described in the application notes: AN4061, AN3969, AN2594, AN3390, AN4056) for STM32F091
Note that in the F091 flash pages are smaller than in the F401.
2KB which gives less than 1KB for variables.
Thanks, NS. I will test and report back... It works as is! I will try in my program...
QUESTION: Why does your printf work? For printing, I have to do: Serial pc(USBTX, USBRX);
..and then print with: pc.print()
posted by 23 Sep 2016QUESTION 2: How can I increase EEPROM emulation (flash) size? I need more than 2 pages. (2KB) The app not AN4061 says it should be done, but not how.
posted by 23 Sep 20161. This version of the printf function works much like the class method Serial. It operates at Nucleo F091 on default parameters, ie. USBTX, USBRX 9600.
It should work. If this does not work, use the Serial class.
2. In this program here above(00_eeprom_emulation_f091) I added the possibility of increasing the size of EEPROM. Download the new version.
I have not tested this on the F091. But a similar program (in F030 flash pages are smaller than the F091) I have tested at Nucleo F030 and works well.
I will set NB_OF_VAR to the number of 16-bit variables that I need for storage (eg. 1000), right? And so VirtAddVarTab[NB_OF_VAR] must have 1000 elements, that is: VirtAddVarTab[1000] = { 1, 2, 3, ..., 999, 1000 }; Right?
What is maximum number (NB_OF_VAR) of 16-bit variables ? 65535?
posted by 20 Dec 2016Yes. If you need to increase this variable (in eeprom.h), then at the same time increase the number of array elements in VirtAddVarTab.
Although NB_OF_VAR is declared in #define macro as uint8_t, I suspect that you can use numbers range 1 ... 32768.
But the main limitation is the size of the "virtual" page ie. number of variables is the size of "virtual" page (in bytes) divided by four.
Where the virtual page size, is the size of flash pages multiplied by PAGE_NB_PVP. What can you change.
I set EEPROM_START_ADDRESS and PAGE0.. = ADDR_FLASH_PAGE_118 and PAGE1_.. to ADDR_FLASH_PAGE_121 to move flash data away from code. NB_OF_VAR = 1000 reads and writes 1000 words ok. But NB_OF_VAR = 2000 fails
posted by 20 Dec 2016If you do not have not changed PAGE_NB_PVP = 4, then page1 should start from (at least) ADDR_FLASH_PAGE_122, not 121.
PAGE0 - flash page 118,119,120,121
PAGE1 - Flash pages 122,123,124,125
With NB_OF_VAR = 1000, all works ok.. no delays. Writing takes a second or two. With NB_OF_VAR = 2000, then writing takes 2-3 minutes, using EE_WriteVariable.
Here is my eeprom.h ....................................
define PAGE_NB_PVP ((uint32_t) 4)
define PAGE_SIZE ((uint32_t)FLASH_PAGE_SIZE * PAGE_NB_PVP) /* Page size */
define EEPROM_START_ADDRESS ((uint32_t)ADDR_FLASH_PAGE_118)
define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000)) define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
define PAGE1_BASE_ADDRESS ((uint32_t)(ADDR_FLASH_PAGE_122)) define PAGE1_END_ADDRESS ((uint32_t)(ADDR_FLASH_PAGE_122 + PAGE_SIZE - 1))
/* Used Flash pages for EEPROM emulation */ define PAGE0 ((uint16_t)0x0000) define PAGE1 ((uint16_t)((PAGE1_BASE_ADDRESS-PAGE0_BASE_ADDRESS)/PAGE_SIZE)) /* Page nb between PAGE0_BASE_ADDRESS &
PAGE1_BASE_ADDRESS*/
/* No valid page define */ define NO_VALID_PAGE ((uint16_t)0xFFFF)
/* Page status definitions */ define ERASED ((uint16_t)0xFFFF) /* Page is empty */ define RECEIVE_DATA ((uint16_t)0xEEEE) /* Page is marked to receive data */ define VALID_PAGE ((uint16_t)0x0000) /* Page containing valid data */
/* Valid pages in read and write defines */ define READ_FROM_VALID_PAGE ((uint8_t)0x00) define WRITE_IN_VALID_PAGE ((uint8_t)0x01)
/* Page full define */ define PAGE_FULL ((uint8_t)0x80)
/* Variables' number */ define NB_OF_VAR ( 2000 )
posted by 25 Dec 2016I tried to create a situation like yours, but I don't have the F091. I did tests on the Nucleo-F030 (the only difference is a different flash page size) using this program:
#include "mbed.h" #include "eeprom.h" Timer t; uint16_t VirtAddVarTab[NB_OF_VAR]; int main() { int i; printf("\n\n*** EEPROM test ***\n"); printf("NB_OF_VAR=%d, PAGE_SIZE=%d, FLASH_PAGE_SIZE=%d\n\n", NB_OF_VAR,PAGE_SIZE,FLASH_PAGE_SIZE); for (i=0; i<NB_OF_VAR; i++) { VirtAddVarTab[i]=i; } // virtual addreses in sequence 0..NB_OF_VAR-1 printf("EEPROM init "); t.reset(); t.start(); /* Unlock the Flash Program Erase controller */ HAL_FLASH_Unlock(); EE_Init(); t.stop(); printf("- time used= %f seconds\n", t.read()); printf("First writing (all variables) "); t.reset(); t.start(); for (i=0; i<NB_OF_VAR; i++) { EE_WriteVariable(VirtAddVarTab[i], 10000+i); } // t.stop(); printf("- time used= %f seconds\n", t.read()); printf("Second writing (all variables) "); t.reset(); t.start(); for (i=0; i<NB_OF_VAR; i++) { EE_WriteVariable(VirtAddVarTab[i], 20000+i); } // t.stop(); printf("- time used= %f seconds\n", t.read()); printf("Third writing (all variables) "); t.reset(); t.start(); for (i=0; i<NB_OF_VAR; i++) { EE_WriteVariable(VirtAddVarTab[i], 30000+i); } // t.stop(); printf("- time used= %f seconds\n", t.read()); printf("The end.\n"); }
where I used 2,000 variables and size of virtual page 8kB.
Results:
*** EEPROM test *** NB_OF_VAR=2000, PAGE_SIZE=8192, FLASH_PAGE_SIZE=1024 EEPROM init - time used= 0.001267 seconds First writing (all variables) - time used= 0.700537 seconds Second writing (all variables) - time used= 2.010621 seconds Third writing (all variables) - time used= 0.964252 seconds The end. *** EEPROM test *** NB_OF_VAR=2000, PAGE_SIZE=8192, FLASH_PAGE_SIZE=1024 EEPROM init - time used= 0.000498 seconds First writing (all variables) - time used= 0.964254 seconds Second writing (all variables) - time used= 0.964421 seconds Third writing (all variables) - time used= 0.964248 seconds The end.
The first pass is after flashing the program, when the EEPROM is uninitialized. The second after the reset.
Therefore, I don't know what might be in your case, that you have such long writing times (minutes).
8 years, 6 months ago.
If I were you, I'll use RTC backup registers.
https://developer.mbed.org/users/gregeric/notebook/using-stm32-rtc-backup-registers/