Как использовать резервную SRAM как EEPROM в STM32F4

Существует два способа эмуляции EEPROM на STM32F4:

  • Встроенная резервная копия SRAM на 4 Кбайта
  • Встроенная флэш-память с определенным программным алгоритмом

Второй вариант описан здесь: AN3969.

Но Google, к сожалению, не смог предоставить информацию о том, как использовать первый вариант - используя резервную SRAM 4Kb как EEPROM?..

Может ли кто-нибудь помочь по этой теме?

Ответ 1

должен выполнить следующие действия:

  • Включить часы PWR

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
  • Разрешить доступ к домену резервного копирования

    PWR_BackupAccessCmd(ENABLE);
    
  • Включить резервное время SRAM

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
    
  • Включите резервный SRAM с низким энергопотреблением, чтобы сохранить его в режиме VBAT.

    PWR_BackupRegulatorCmd(ENABLE);
    

и вы можете писать/читать данные для sram (эти коды из кодов BKP_Domain в STM32F4xx_DSP_StdPeriph_Lib) (в моем 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 datasheet Я согласен, что неясно, как на самом деле использовать резервный sram (или где он находится). Вот что я нашел. Оба регистратора RTC и резервная SRAM содержат некоторое количество хранилища, которое поддерживается до тех пор, пока у вас есть питание от батареи. RTC содержит 20 регистров (80 байт), а резервный sram (который является его собственным периферийным устройством на AHB1 и расположенным в области адресов регистров) содержит 0x1000 (4096 байт). По умолчанию они не включены.

в 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).

на стр. 71 таблицы данных и p65 справочного руководства

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

и стр. 73 таблицы данных и p67 справочного руководства

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

Страница 118-119 справочного руководства содержит информацию о включении резервных SRAM и регистров RTC.

ПРИМЕЧАНИЕ. Если вы уже используете RTC в домене резервного копирования и вам нужно хранить <= 80 байт, тогда вам лучше использовать регистры резервного копирования RTC, поскольку включение резервного SRAM будет в основном удвоить текущее потребление (см. таблицу 25 в техническом описании stm32f405/7).

вот мои функции записи и чтения как для резервного 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 soft reset. Начальный загрузчик всегда начинается первым. Он проверяет "магическое число", если он присутствует, он выполняет, иначе запускает основную программу

при использовании 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

В настоящее время я использую микроконтроллер STM32F2xx. Согласно техническому описанию:

4-Кбайтная резервная SRAM - это область, подобная EEPROM.

Чтобы сохранить содержимое резервных копий RTC... когда VDD выключен, вывод VBAT можно подключить к дополнительному резервному напряжению, подаваемому батареей или другим источником.

Суперкап, например, потребуется для хранения содержимого резервных регистров при выключении микроконтроллера.

Кроме того, согласно документу:

После reset резервный домен (... backup 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;

Ответ 5

Вот пример библиотеки 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;
}

Ответ 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;

In Init:

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

Ответ 7

Конфигурация HAL для STM32H7 для доступа к резервной 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;

}

В дополнение к этому вам нужно добавить следующую строку в systemInit(), чтобы разрешить сквозной доступ к резервной копии SRAM.

SCB->CACR |= 1<<2;

Ответ 8

К сожалению выражение: * (__IO uint8_t *) 0x40024000 = 42; генерирует ошибку, даже если я "__IO" заменил на "volatile".