如何在STM32F4中使用Backup SRAM作为EEPROM

Posted

技术标签:

【中文标题】如何在STM32F4中使用Backup SRAM作为EEPROM【英文标题】:How to use Backup SRAM as EEPROM in STM32F4 【发布时间】:2014-01-07 04:45:53 【问题描述】:

在STM32F4上模拟EEPROM有两种方式:

    片上 4 KB 备份 SRAM 片上闪存,具有特定的软件算法

此处描述了第二个选项:AN3969。

但不幸的是,谷歌无法提供有关如何使用第一个选项的信息 - 使用 4Kb 的备份 SRAM 作为 EEPROM?..

任何人都可以就这个话题提供帮助吗?

【问题讨论】:

你有stm32 f4系列库还是要自己修改外设寄存器? 【参考方案1】:

必须这样做:

    启用 PWR 时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    

    启用对备份域的访问

    PWR_BackupAccessCmd(ENABLE);
    

    启用备份 SRAM 时钟

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
    

    启用备份 SRAM 低功耗稳压器以在 VBAT 模式下保留其内容

    PWR_BackupRegulatorCmd(ENABLE);
    

您可以将数据写入/读取到 sram(这些代码来自 STM32F4xx_DSP_StdPeriph_Lib 中的 BKP_Domain 代码)(在我的 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++;
          
   

如果你愿意的话:

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

您可以在 STM32F4xx_DSP_StdPeriph_Lib 中找到这些函数。

【讨论】:

【参考方案2】:

在阅读了 stm32f4 参考手册和 stm32f405xx/stm32f407xx 数据表后,我同意目前尚不清楚如何实际使用备份 sram(或其所在位置)。这是我发现的。 RTC 寄存器和备份 SRAM 都包含一定数量的存储空间,只要您有电池供电,这些存储空间就会一直保持。 RTC 包含 20 个寄存器(80 字节),备份 sram(它是 AHB1 上它自己的外设,位于寄存器地址区域内)包含 0x1000(4096 字节)。默认情况下两者均未启用。

在 DM00037051(stm32f405xx/stm32f407xx 数据表,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).

数据表第 71 页和参考手册第 65 页

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

数据表第 73 页和参考手册第 67 页

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

参考手册的第 118-119 页包含有关启用备份 SRAM 和 RTC 寄存器的信息。

注意:如果您已经在备份域中使用 RTC 并且只需要存储

这是我对备份 SRAM 和备份 RTC 寄存器的读写功能

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;

【讨论】:

【参考方案3】:

我必须根据用户请求从主程序跳转到引导加载程序。 所以我在主程序中将一些“幻数”放入 BKPSRAM 中,进行 CPU 软复位。 引导加载程序总是首先启动。 它检查“幻数”是否存在,它执行,否则启动主程序

使用 HAL 时,这是如何跳转到引导加载程序:

__HAL_RCC_PWR_CLK_ENABLE();

HAL_PWR_EnableBkUpAccess();

__BKPSRAM_CLK_ENABLE();

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

HAL_NVIC_SystemReset();

在引导加载程序内部读取幻数就足以启用备份 sram 时钟(引导加载程序使用 StdPeriphDriver)。

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);

extRequest = *(__IO uint8_t *)0x40024000;

if(extRequest == 42)
    //run bootloader

cpu 是 stm32f407

【讨论】:

【参考方案4】:

这里是 HAL 库使用备份 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;

【讨论】:

这对我来说非常有帮助,值得更多曝光。谢谢。 很高兴知道您发现这很有帮助【参考方案5】:

我目前正在使用 STM32F2xx 微控制器。根据数据表:

4 KB 备份 SRAM 是一个类似 EEPROM 的区域。

为了保留 RTC 备份寄存器的内容……当 VDD 关闭时,VBAT 引脚可以连接到由电池或其他电源提供的可选待机电压。

例如,当微控制器断电时,将需要一个超级电容器来维持备份寄存器的内容。

另外,根据文档:

重置后,备份域(……备份 SRAM)受到保护,防止可能的不必要的写访问。要启用对备份域的访问,请执行以下操作……

它为您提供了有关如何通过直接写入特定外设寄存器来访问备份域的说明。如果您可以访问 STM32F4xx 库,则可以这样调用(注意:我使用的是 STM32F2xx 库):

PWR_BackupAccessCmd(ENABLE);

注意:除了简单地调用上述函数之外,还有更多内容,例如启用备份 SRAM 接口时钟。请查阅 STM32F4 系列文档。

库源代码中嵌入了许多非常宝贵的文档,如果可用,应该阅读。

在 STM32F2 系列微控制器上,SRAM 位于以下内存地址范围:

0x40024000 - 0x40024FFF

并且可以写到某个位置的某处,例如如下:

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

【讨论】:

【参考方案6】:

有用的例子 在标题中:

//------------------------------------
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;
//------------------------------------

在代码头中 定义为数据:

sBKPSRAM    *BKPSRAM    = (sBKPSRAM *)BKPSRAM_BASE;

在初始化中:

void main(void)

(....)
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
  PWR_BackupAccessCmd(ENABLE);
  PWR_BackupRegulatorCmd(ENABLE);
  ifDefault();
(....)

过程中:

//-------------------------------------------------
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;
        

//-------------------------------------------------

【讨论】:

这个问题是 5 年前回答的【参考方案7】:

STM32H7 访问备份 SRAM 的 HAL 配置:

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

Main() 

__HAL_RCC_BKPRAM_CLK_ENABLE();

HAL_PWREx_EnableBkUpReg();

BKP_RAM = 0xA5AA5A55;


除此之外,您还需要在 systemInit() 中添加以下行以启用对备份 SRAM 的直写访问。

SCB->CACR |= 1<<2;

【讨论】:

以上是关于如何在STM32F4中使用Backup SRAM作为EEPROM的主要内容,如果未能解决你的问题,请参考以下文章

STM32F429第二十四篇之SRAM原理

STM32F429第二十四篇之SRAM原理

如何使用STM32F4的DSP库

如何在 STM32F4 的 keil uvision 5.30 中“不支持命令”

如何将 STM32f4 编程为 SPI 从设备

FMC—扩展外部 SDRAM