STM32 Risparmio energetico: registro di backup RTC e conservazione della SRAM – 9


STM32 power saving: RTC backup register and SRAM preservation
STM32 power saving: RTC backup register and SRAM preservation

Infine, esaminiamo il dominio di backup per risolvere il problema della conservazione dello stato attraverso le modalità di sospensione, prima il registro di backup RTC e poi il backup della memoria SRAM.

Verifica quale tipo di backup è disponibile su STM32

STM32: power supply and backup domain schema
STM32: power supply and backup domain schema

Per conservare lo stato in spegnimento/standby puoi utilizzare (come descritto nell’introduzione) il registro RTC/TAMP o la SRAM.

Ecco lo STM32 e ST-Link V2 utilizzati in questo test STM32F103C8T6 STM32F401 STM32F411 ST-Link v2 ST-Link v2 official

Qui l'FTDI AliExpress USB to TTL CH340G - AliExpress USB to TTL FT232RL


Ecco il mio multimetro Aneng SZ18

Ecco un semplice sketch per verificare il tuo STM32.

/**
 * A simple sketch to check witch kind of backup you can use
 * on your device
 *
 * Renzo Mischianti <www.mischianti.org>
 * en: https://mischianti.org/category/tutorial/stm32-tutorial/
 * it: https://mischianti.org/it/category/guide/guida-alla-linea-di-microcontrollori-stm32/
 *
 */

#include "STM32LowPower.h"

void printBackupRegisterType();
void printSRAMAvailability();

void setup()
{
  Serial.begin(115200);
  delay(1000);

  Serial.println(F("------------------------------------------------"));
  printBackupRegisterType();
  printSRAMAvailability();
  Serial.println(F("------------------------------------------------"));

}

void loop()
{
  delay(1000);
}

void printBackupRegisterType() {
#if defined(BKP_BASE)
	Serial.println(F("Backup base enabled!"));
#elif defined(RTC_BKP0R)
	Serial.println(F("Backup RTC enabled!"));
#elif defined(TAMP_BKP0R)
#if defined(STM32G4xx) || defined(STM32L5xx) || defined(STM32U5xx) ||\
    defined(STM32MP1xx) || defined(STM32WLxx)
  /* For those series this API requires RTC even if it is not used
     and TAMP is used instead */
	Serial.println(F("Backup RTC and TAMP enabled! TAMP busy use RTC!"));
#else
	Serial.println(F("Backup RTC and TAMP enabled! Use TAMP!"));
#endif
#else
	Serial.println(F("Backup not available!"));
#endif

}

void printSRAMAvailability() {
#if defined(BKPSRAM_BASE)
	Serial.println(F("SRAM available!"));
#else
	Serial.println(F("SRAM not available!"));
#endif
}

Nel mio caso con un STM32F401 ottengo questo risultato :(.

------------------------------------------------
Backup RTC enabled!
SRAM not available!
------------------------------------------------

Quindi posso usare solo il registro RTC, ma provo a darti un esempio SRAM.

Utilizzo di un registro di backup per preservare i dati durante lo spegnimento/standby

Ora aggiungeremo i dati nella struttura dati sotto l’area di backup. Come descritto è possibile utilizzare (se presente) RTC Backup Register o SRAM; controlla il paragrafo con lo schizzo per verificare quale ha il tuo microcontrollore.

/**
 * A simple sketch that set the time to
 * 2022-04-20 at 16:00:00 and an alarm at 16:00:10
 * the result is the interrupt after 10 secs
 * We use RTC backup register to preserve state of a variable
 * across the shutdown and count the restart
 * The valiable is resetted every time you disconnect the power.
 *
 * Renzo Mischianti <www.mischianti.org>
 * https://mischianti.org
 *
 */

#include "STM32LowPower.h"
#include <STM32RTC.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 16;

/* Change these values to set the current initial date */
const byte day = 20;
const byte month = 4;
const byte year = 22;

void alarmMatch(void *data);
void printWakeSource();
void printBackupRegisterType();
void printClockReady();

void setup()
{
  Serial.begin(115200);

  Serial.println(F("------------------------------------------------"));
  printWakeSource();
  printBackupRegisterType();
  printClockReady();
  Serial.println(F("------------------------------------------------"));

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
	  enableBackupDomain();
	  resetBackupDomain();

	  setBackupRegister(LL_RTC_BKP_DR0, 0);
  }
  __HAL_RCC_CLEAR_RESET_FLAGS();

  // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.
  // By default the LSI is selected as source.
//  rtc.setClockSource(STM32RTC::LSI_CLOCK);

  rtc.begin(); // initialize RTC 24H format
  // we set the time at 2022-04-20 at 16:00:00
  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(day, month, year);

  delay(1000);


  String someRandomData = "www.mischianti.org";

  // Configure low power
  LowPower.begin();
  LowPower.enableWakeupFrom(&rtc, alarmMatch, &someRandomData);

  int wakeSeconds = rtc.getSeconds()+20;
  // Now we set an alert at 16:00:10
  // pratically 10 secs after the start
  // (check the initialization of clock)
  rtc.setAlarmDay(day);
  rtc.setAlarmTime(16, 0, wakeSeconds, 0);
  rtc.enableAlarm(rtc.MATCH_DHHMMSS);

  Serial.print("Boot number RTC backup register: ");
//  uint32_t boot_count_RTC_back = rtc_read_backup_reg(RTC_BKP_DR1);
  uint32_t boot_count_RTC_back = getBackupRegister(LL_RTC_BKP_DR0);
  Serial.println(boot_count_RTC_back);
  boot_count_RTC_back = boot_count_RTC_back + 1;
//  rtc_write_backup_reg(RTC_BKP_DR1, boot_count_RTC_back);
  setBackupRegister(LL_RTC_BKP_DR0, boot_count_RTC_back);

  Serial.println("Start shutdown mode in ");
  for (int i = 10;i>0;i--) { Serial.print(i); Serial.print(" "); delay(1000); } Serial.println( "OK!" );
  // Print date...
  Serial.printf("Now is %02d/%02d/%02d %02d:%02d:%02d.%03d and we set the wake at 16:10! So wait 10secs! \n",
		  rtc.getDay(), rtc.getMonth(), rtc.getYear(),
		  rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());
  delay(1000);

  LowPower.shutdown();
}

void loop()
{
  // Print date...
  Serial.printf("%02d/%02d/%02d ", rtc.getDay(), rtc.getMonth(), rtc.getYear());

  // ...and time
  Serial.printf("%02d:%02d:%02d.%03d\n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());
  delay(1000);
}

void alarmMatch(void *data)
{
	String myData = *(String*)data;
	Serial.println("Alarm Match!");
	Serial.println(myData);
}

void printClockReady() {
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY)) {
	    Serial.println(F("HSI oscillator clock ready."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY)) {
	    Serial.println(F("HSE oscillator clock ready."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY)) {
	    Serial.println(F("Main PLL clock ready."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLI2SRDY)) {
	    Serial.println(F("PLLI2S clock ready."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY)) {
	    Serial.println(F("LSE oscillator clock ready."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY)) {
	    Serial.println(F("LSI oscillator clock ready."));
	}
}

void printWakeSource() {
	Serial.println(F("-----------------------------------"));
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
	    Serial.println(F("Wake from POR/PDR or BOR reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) {
	    Serial.println(F("Wake from Pin reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) {
	    Serial.println(F("Wake from POR/PDR reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) {
	    Serial.println(F("Wake from Software reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) {
	    Serial.println(F("Wake from Independent Watchdog reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) {
	    Serial.println(F("Wake from Window Watchdog reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) {
	    Serial.println(F("Wake from Low Power reset."));
	}
	Serial.println(F("-----------------------------------"));
}

void printBackupRegisterType() {
#if defined(BKP_BASE)
	Serial.println(F("Backup base enabled!"));
#elif defined(RTC_BKP0R)
	Serial.println(F("Backup RTC enabled!"));
#elif defined(TAMP_BKP0R)
#if defined(STM32G4xx) || defined(STM32L5xx) || defined(STM32U5xx) ||\
    defined(STM32MP1xx) || defined(STM32WLxx)
  /* For those series this API requires RTC even if it is not used
     and TAMP is used instead */
	Serial.println(F("Backup RTC and TAMP enabled! TAMP busy use RTC!"));
#else
	Serial.println(F("Backup RTC and TAMP enabled! Use TAMP!"));
#endif
#else
	Serial.println(F("Backup not available!"));
#endif

}

Puoi vedere che ripristiniamo la variabile ogni volta che scolleghiamo il dispositivo dall’alimentazione.

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
	  enableBackupDomain();
	  resetBackupDomain();

	  setBackupRegister(LL_RTC_BKP_DR0, 0);
  }
  __HAL_RCC_CLEAR_RESET_FLAGS();

Quando il dispositivo viene ricollegato all’alimentazione (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) abilitiamo il backup (enableBackupDomain(); puoi usarlo disableBackupDomain(); per disabilitarlo) e inizializziamo la variabile a 0 (setBackupRegister(LL_RTC_BKP_DR0, 0);).

Quindi leggiamo i dati dal registro, stampiamo i dati, aggiungiamo uno alla variabile e lo impostiamo nel registro.

  Serial.print("Boot number RTC backup register: ");
//  uint32_t boot_count_RTC_back = rtc_read_backup_reg(RTC_BKP_DR1);
  uint32_t boot_count_RTC_back = getBackupRegister(LL_RTC_BKP_DR0);
  Serial.println(boot_count_RTC_back);
  boot_count_RTC_back = boot_count_RTC_back + 1;
//  rtc_write_backup_reg(RTC_BKP_DR1, boot_count_RTC_back);
  setBackupRegister(LL_RTC_BKP_DR0, boot_count_RTC_back);

Per ottenere il valore dal registro utilizziamo uint32_t boot_count_RTC_back = getBackupRegister(LL_RTC_BKP_DR0);.

L’uscita seriale dopo la prima interruzione dell’alimentazione è:

------------------------------------------------
-----------------------------------
Wake from POR/PDR or BOR reset.
Wake from Pin reset.
Wake from POR/PDR reset.
-----------------------------------
Backup RTC enabled!
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
------------------------------------------------
Boot number RTC backup register: 0
Start shutdown mode in 
10 9 8 7 6 5 4 3 2 1 OK!
Now is 20/04/22 16:00:10.408 and we set the wake at 16:10! So wait 10secs! 
------------------------------------------------
-----------------------------------
-----------------------------------
Backup RTC enabled!
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
LSI oscillator clock ready.
------------------------------------------------
Boot number RTC backup register: 1
Start shutdown mode in 
10 9 8 7 6 5 4 3 2 1 OK!
Now is 20/04/22 16:00:10.404 and we set the wake at 16:10! So wait 10secs! 
------------------------------------------------
-----------------------------------
-----------------------------------
Backup RTC enabled!
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
LSI oscillator clock ready.
------------------------------------------------
Boot number RTC backup register: 2
Start shutdown mode in 
10 9 8 7 6 5 4 3 2 1 OK!
Now is 20/04/22 16:00:10.388 and we set the wake at 16:10! So wait 10secs! 
------------------------------------------------
-----------------------------------
-----------------------------------
Backup RTC enabled!
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
LSI oscillator clock ready.
------------------------------------------------
Boot number RTC backup register: 3
Start shutdown mode in 
10 9 8 7 6 5 4 3 2 1 OK!
Now is 20/04/22 16:00:10.400 and we set the wake at 16:10! So wait 10secs! 

Utilizzo della memoria SRAM per preservare i dati durante lo spegnimento/standby

Come già descritto non ho un dispositivo che supporti SRAM, quindi provo a fare un esempio solo con documentazione senza test.

/**
 * A simple sketch that set the time to
 * 2022-04-20 at 16:00:00 and an alarm at 16:00:10
 * the result is the interrupt after 10 secs
 * We use SRAM to preserve state of a structure
 * across the shutdown and count the restart
 * The structure is resetted every time you disconnect the power.
 *
 * Renzo Mischianti <www.mischianti.org>
 * https://mischianti.org
 *
 */

#include "STM32LowPower.h"
#include <STM32RTC.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 16;

/* Change these values to set the current initial date */
const byte day = 20;
const byte month = 4;
const byte year = 22;

void alarmMatch(void *data);
void printWakeSource();
void printSRAMAvailability();

typedef struct Info
  {
  char url[19];                           // Change
  int deviceID;
  int count;
  };

void setup()
{
  Serial.begin(115200);

  Serial.println(F("------------------------------------------------"));
  printWakeSource();
  printSRAMAvailability();
  Serial.println(F("------------------------------------------------"));

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
	  enableBackupDomain();
	  resetBackupDomain();

	  struct Info info = {"www.mischianti.org", 1, 0};
	  writeBackupSRAM(0, (uint32_t*)&info, sizeof(info));

  }
  __HAL_RCC_CLEAR_RESET_FLAGS();

  // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.
  // By default the LSI is selected as source.
//  rtc.setClockSource(STM32RTC::LSI_CLOCK);

  rtc.begin(); // initialize RTC 24H format
  // we set the time at 2022-04-20 at 16:00:00
  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(day, month, year);

  delay(1000);


  String someRandomData = "www.mischianti.org";

  // Configure low power
  LowPower.begin();
  LowPower.enableWakeupFrom(&rtc, alarmMatch, &someRandomData);

  int wakeSeconds = rtc.getSeconds()+20;
  // Now we set an alert at 16:00:10
  // pratically 10 secs after the start
  // (check the initialization of clock)
  rtc.setAlarmDay(day);
  rtc.setAlarmTime(16, 0, wakeSeconds, 0);
  rtc.enableAlarm(rtc.MATCH_DHHMMSS);

  Serial.print("Boot number SRAM: ");
  struct Info info;
  readBackupSRAM(0, (uint32_t*)&info, sizeof(info));
  Serial.println(info.count);
  info.count = info.count + 1;
  writeBackupSRAM(0, (uint32_t*)&info, sizeof(info));

  Serial.println("Start shutdown mode in ");
  for (int i = 10;i>0;i--) { Serial.print(i); Serial.print(" "); delay(1000); } Serial.println( "OK!" );
  // Print date...
  Serial.printf("Now is %02d/%02d/%02d %02d:%02d:%02d.%03d and we set the wake at 16:10! So wait 10secs! \n",
		  rtc.getDay(), rtc.getMonth(), rtc.getYear(),
		  rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());
  delay(1000);

  LowPower.shutdown();
}

void loop()
{
  // Print date...
  Serial.printf("%02d/%02d/%02d ", rtc.getDay(), rtc.getMonth(), rtc.getYear());

  // ...and time
  Serial.printf("%02d:%02d:%02d.%03d\n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());
  delay(1000);
}

void alarmMatch(void *data)
{
	String myData = *(String*)data;
	Serial.println("Alarm Match!");
	Serial.println(myData);
}

void printWakeSource() {
	Serial.println(F("-----------------------------------"));
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
	    Serial.println(F("Wake from POR/PDR or BOR reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) {
	    Serial.println(F("Wake from Pin reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) {
	    Serial.println(F("Wake from POR/PDR reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) {
	    Serial.println(F("Wake from Software reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) {
	    Serial.println(F("Wake from Independent Watchdog reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) {
	    Serial.println(F("Wake from Window Watchdog reset."));
	}
	if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) {
	    Serial.println(F("Wake from Low Power reset."));
	}
	Serial.println(F("-----------------------------------"));
}

void printSRAMAvailability() {
#if defined(BKPSRAM_BASE)
	Serial.println(F("SRAM available!"));
#else
	Serial.println(F("SRAM not available!"));
#endif
}

Grazie

  1. STM32F1 Blue Pill: piedinatura, specifiche e configurazione IDE Arduino (STM32duino e STMicroelectronics)
  2. STM32: programmazione (STM32F1) via USB con bootloader STM32duino
  3. STM32: programmazione (STM32F1 STM32F4) tramite USB con bootloader HID
  4. STM32F4 Black Pill: pinout, specifiche e configurazione IDE Arduino
  5. STM32: ethernet w5500 standard (HTTP) e SSL (HTTPS)
  6. STM32: ethernet enc28j60 standard (HTTP) e SSL (HTTPS)
  7. STM32: WiFiNINA con un ESP32 come WiFi Co-Processor
    1. STM32F1 Blue-pill: shield WiFi (WiFiNINA)
    2. STM32F4 Black-pill: shield WiFi (WiFiNINA)
  8. Come utilizzare la scheda SD con l’stm32 e la libreria SdFat
  9. STM32: memoria flash SPI FAT FS
  10. STM32: RTC interno, sistema orario e backup batteria (VBAT)
  11. STM32 LoRa
  1. STM32 Risparmio energetico
    1. STM32F1 Blue-Pill gestione clock e frequenza
    2. STM32F4 Black-Pill gestione clock e frequenza
    3. Introduzione e framework Arduino vs STM
    4. Libreria LowPower, cablaggio e Idle (STM Sleep).
    5. Sleep, deep sleep, shutdown e consumo energetico
    6. Sveglia da allarme RTC e Seriale
    7. Sveglia da sorgente esterna
    8. Introduzione al dominio di backup e conservazione delle variabili durante il RESET
    9. Registro di backup RTC e conservazione della SRAM

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *