How to use Backup SRAM as EEPROM in STM32F4

44.1k views Asked by At

There are two ways of emulating EEPROM on the STM32F4:

  1. On-chip 4 Kbytes backup SRAM
  2. On-chip Flash, with specific software algorithm

The second option is described here: AN3969.

But google, unfortunately, hasn't been able to provide information on how to use the first option - using the 4Kb of backup SRAM as EEPROM?..

Can anyone help on the topic?

7

There are 7 answers

0
ytasan On BEST ANSWER

must do these:

  1. Enable the PWR clock

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
  2. Enable access to the backup domain

    PWR_BackupAccessCmd(ENABLE);
    
  3. Enable backup SRAM Clock

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
    
  4. Enable the Backup SRAM low power Regulator to retain it's content in VBAT mode

    PWR_BackupRegulatorCmd(ENABLE);
    

and you can write/read datas to sram (these codes from BKP_Domain codes in STM32F4xx_DSP_StdPeriph_Lib) (in my mcu stm32f417 BKPSRAM_BASE=0x40024000)

   // Write to Backup SRAM with 32-Bit Data 
   for (i = 0x0; i < 0x100; i += 4) {
       *(__IO uint32_t *) (BKPSRAM_BASE + i) = i;
   }

   // Check the written Data 
   for (i = 0x0; i < 0x100; i += 4) {
          if ((*(__IO uint32_t *) (BKPSRAM_BASE + i)) != i){
              errorindex++;
          }
   }

then if you want:

    // Wait until the Backup SRAM low power Regulator is ready
    while(PWR_GetFlagStatus(PWR_FLAG_BRR) == RESET)
    {}

you can find these functions in STM32F4xx_DSP_StdPeriph_Lib.

0
Fiddling Bits On

I'm currently using the an STM32F2xx microcontroller. According to the datasheet:

The 4-Kbyte backup SRAM is an EEPROM-like area.

To retain the content of the RTC backup registers … when VDD is turned off, VBAT pin can be connected to an optional standby voltage supplied by a battery or by another source.

A supercap, for example, would be required to maintain the contents of the backup registers while the microcontroller is powered off.

Also, according to the document:

After reset, the backup domain (… backup SRAM) is protected against possible unwanted write accesses. To enable access to the backup domain, proceed as follows …

It gives you instructions on how to gain access to the backup domain by directly writing to the certain peripheral register. If you have access to the STM32F4xx library, you can call something like this (note: I'm using the STM32F2xx library):

PWR_BackupAccessCmd(ENABLE);

Note: There's is more to it than simply calling the above function, such as enabling the backup SRAM interface clock. Consult the STM32F4 series documentation.

There is a lot of documentation embedded in the library source that is invaluable and if it's available should be read.

On the STM32F2 series microcontroller, SRAM is located at the following memory address range:

0x40024000 - 0x40024FFF

And can be written to somewhere at location, for example, as follows:

#define VAR_LOC ((volatile uint8_t *)(0x40024000))
volatile uint8_t *pVar = VAR_LOC;
*pVar = 5;
0
swinman On

after reading through the Reference Manual for stm32f4 and the stm32f405xx/stm32f407xx datasheet I agree that it isn't clear how to actually use the backup sram (or where it is located). Here is what I found. Both the RTC registers and backup SRAM contain some amount of storage that is maintained as long as you have battery power. The RTC contains 20 registers (80 bytes) and the backup sram (which is its own peripheral on AHB1 and located within the register address region) contains 0x1000 (4096 bytes). Neither are enabled by default.

in DM00037051 (stm32f405xx/stm32f407xx datasheet, p29):

The 4-Kbyte backup SRAM is an EEPROM-like memory area. It can be used to store
data which need to be retained in VBAT and standby mode. This memory area is 
disabled by default to minimize power consumption (see Section 2.2.19: 
Low-power modes). It can be enabled by software.

The backup registers are 32-bit registers used to store 80 bytes of user 
application data when VDD power is not present. Backup registers are not reset
by a system, a power reset, or when the device wakes up from the Standby mode 
(see Section 2.2.19: Low-power modes).

on page 71 of datasheet and p65 of the reference manual

AHB1   |   0x4002 4000 - 0x4002 4FFF   |   BKPSRAM

and page 73 of the datatasheet and p67 of the reference manual

APB1   |   0x4000 2800 - 0x4000 2BFF   |   RTC & BKP Registers

Page 118-119 of the reference manual contains information on enabling the backup SRAM and RTC registers.

NOTE: if you are already using the RTC in the backup domain and only need to store <= 80 bytes, then you are better off using the RTC backup registers because enabling the backup SRAM will basically double the current consumption (see table 25 in the stm32f405/7 datasheet).

here are my write and read functions for both backup SRAM and backup RTC registers

int8_t write_to_backup_sram( uint8_t *data, uint16_t bytes, uint16_t offset ) {
  const uint16_t backup_size = 0x1000;
  uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
  uint16_t i;
  if( bytes + offset >= backup_size ) {
    /* ERROR : the last byte is outside the backup SRAM region */
    return -1;
  }
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
  /* disable backup domain write protection */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);   // set RCC->APB1ENR.pwren
  PWR_BackupAccessCmd(ENABLE);                          // set PWR->CR.dbp = 1;
  /** enable the backup regulator (used to maintain the backup SRAM content in
    * standby and Vbat modes).  NOTE : this bit is not reset when the device
    * wakes up from standby, system reset or power reset. You can check that
    * the backup regulator is ready on PWR->CSR.brr, see rm p144 */
  PWR_BackupRegulatorCmd(ENABLE);     // set PWR->CSR.bre = 1;
  for( i = 0; i < bytes; i++ ) {
    *(base_addr + offset + i) = *(data + i);
  }
  PWR_BackupAccessCmd(DISABLE);                     // reset PWR->CR.dbp = 0;
  return 0;
}

int8_t read_from_backup_sram( uint8_t *data, uint16_t bytes, uint16_t offset ) {
  const uint16_t backup_size = 0x1000;
  uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
  uint16_t i;
  if( bytes + offset >= backup_size ) {
    /* ERROR : the last byte is outside the backup SRAM region */
    return -1;
  }
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
  for( i = 0; i < bytes; i++ ) {
    *(data + i) = *(base_addr + offset + i);
  }
  return 0;
}

int8_t write_to_backup_rtc( uint32_t *data, uint16_t bytes, uint16_t offset ) {
  const uint16_t backup_size = 80;
  volatile uint32_t* base_addr = &(RTC->BKP0R);
  uint16_t i;
  if( bytes + offset >= backup_size ) {
    /* ERROR : the last byte is outside the backup SRAM region */
    return -1;
  } else if( offset % 4 || bytes % 4 ) {
    /* ERROR: data start or num bytes are not word aligned */
    return -2;
  } else {
    bytes >>= 2;      /* divide by 4 because writing words */
  }
  /* disable backup domain write protection */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);   // set RCC->APB1ENR.pwren
  PWR_BackupAccessCmd(ENABLE);                          // set PWR->CR.dbp = 1;
  for( i = 0; i < bytes; i++ ) {
    *(base_addr + offset + i) = *(data + i);
  }
  PWR_BackupAccessCmd(DISABLE);                     // reset PWR->CR.dbp = 0;
  // consider also disabling the power peripherial?
  return 0;
}

int8_t read_from_backup_rtc( uint32_t *data, uint16_t bytes, uint16_t offset ) {
  const uint16_t backup_size = 80;
  volatile uint32_t* base_addr = &(RTC->BKP0R);
  uint16_t i;
  if( bytes + offset >= backup_size ) {
    /* ERROR : the last byte is outside the backup SRAM region */
    return -1;
  } else if( offset % 4 || bytes % 4 ) {
    /* ERROR: data start or num bytes are not word aligned */
    return -2;
  } else {
    bytes >>= 2;      /* divide by 4 because writing words */
  }
  /* read should be 32 bit aligned */
  for( i = 0; i < bytes; i++ ) {
    *(data + i) = *(base_addr + offset + i);
  }
  return 0;
}
2
Meera On

Here is the example of HAL library to use backup SRAM.

#define  WRITE_READ_ADDR  0x01 //offset value.you can change according to your application
uint32_t write_arr = 0xA5A5A5A6;
uint32_t read_arr;

int main()
{
   enable_backup_sram();
   writeBkpSram(write_arr);
   while(1)
   {
      read_arr = readBkpSram();
   }
}
void enable_backup_sram(void)
{
    /*DBP : Enable access to Backup domain */
    HAL_PWR_EnableBkUpAccess();
    /*PWREN : Enable backup domain access  */
    __HAL_RCC_PWR_CLK_ENABLE();
    /*BRE : Enable backup regulator
      BRR : Wait for backup regulator to stabilize */
    HAL_PWREx_EnableBkUpReg();
   /*DBP : Disable access to Backup domain */
    HAL_PWR_DisableBkUpAccess();
}

void writeBkpSram(uint32_t l_data)
{
   /* Enable clock to BKPSRAM */
  __HAL_RCC_BKPSRAM_CLK_ENABLE();
  /* Pointer write on specific location of backup SRAM */
  (uint32_t *) (BKPSRAM_BASE + WRITE_READ_ADDR) = l_data;
 /* Disable clock to BKPSRAM */
 __HAL_RCC_BKPSRAM_CLK_DISABLE();
}

uint32_t readBkpSram(void)
{
   uint32_t i_retval;

  /* Enable clock to BKPSRAM */
  __HAL_RCC_BKPSRAM_CLK_ENABLE();
  /* Pointer write from specific location of backup SRAM */
  i_retval =  *(uint32_t*) (BKPSRAM_BASE + WRITE_READ_ADDR);
  /* Disable clock to BKPSRAM */
  __HAL_RCC_BKPSRAM_CLK_DISABLE();
  return i_retval;
}
0
SASIDHARAN SR On

HAL Configuration for STM32H7 to access backup SRAM:

#define BKP_RAM (*(__IO uint32_t *) (D3_BKPSRAM_BASE)) //Start address: 0x38800000

Main() {

__HAL_RCC_BKPRAM_CLK_ENABLE();

HAL_PWREx_EnableBkUpReg();

BKP_RAM = 0xA5AA5A55;

}

In addition to that, you need to add a below line in systemInit() to enable write-through access to Backup SRAM.

SCB->CACR |= 1<<2;
0
mkonvisar On

I had to jump from main program to bootloader on user request. So I put some 'magic number' into BKPSRAM in main program, do CPU soft reset. Bootloader always starts first. It checks for 'magic number' if it is present, it executes, else starts main program

when using HAL this is how to jump to bootloader:

__HAL_RCC_PWR_CLK_ENABLE();

HAL_PWR_EnableBkUpAccess();

__BKPSRAM_CLK_ENABLE();

*(__IO uint8_t *)0x40024000 = 42;//magic number

HAL_NVIC_SystemReset();

inside bootloader to read magic number it is enough to enable backup sram clock only (bootloader uses StdPeriphDriver).

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);

extRequest = *(__IO uint8_t *)0x40024000;

if(extRequest == 42)
    //run bootloader

cpu is stm32f407

1
MrTip Sonatson On

Useable example In header:

//------------------------------------
typedef struct
{
    uint32_t    isDefault;          //must by 0x12345678
    uint32_t    LastTestNumber;     
    uint32_t    LastUserNumber;     
    uint32_t    LastModeTest;       
    uint32_t    calibv;
    uint32_t    calibc;
    uint32_t    WorkTime;           
    int32_t     RTCCalib;
    uint32_t    LCDContrast;

} sBKPSRAM;
extern sBKPSRAM *BKPSRAM;//  = (sSDRAM *)SDRAM_BANK_ADDR;
//------------------------------------

In code head define as data:

sBKPSRAM    *BKPSRAM    = (sBKPSRAM *)BKPSRAM_BASE;

In Init:

void main(void)
{
(....)
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
  PWR_BackupAccessCmd(ENABLE);
  PWR_BackupRegulatorCmd(ENABLE);
  ifDefault();
(....)
}

In procedure:

//-------------------------------------------------
void ifDefault(void)
{
    if (BKPSRAM->LastModeTest!=0x12345678)
        {
            printf("BKPSRAM to default\r\n");
            memset(BKPSRAM,0,sizeof(sBKPSRAM));
            BKPSRAM->calibv         =66920;
            BKPSRAM->calibc         =79230;
            BKPSRAM->RTCCalib       =1;
            BKPSRAM->LCDContrast    =2;         
            BKPSRAM->LastModeTest   =0x12345678;
        }
}
//-------------------------------------------------