Site icon Renzo Mischianti

STM32 power saving: backup domain intro and variable preservation across reset – 8

Spread the love

STM32 power saving: backup domain intro, and variable preservation across reset

Another important element of STM32 is the backup domain, but after a brief introduction we are going to evaluate and test the standard solution for RESET, practically the use of variables in “noinit” and “persistent” memory area very interesting features. And for specified usage, we write some simple functions to check the features of our devices.

Variables, RTC register, and SRAM preservation

One of the most useful things you can need when using Low-Power functions is state preservation, and first of all, we are going to do a fast recap of the main Low-Power mode.

Devices feature four main low-power modes:

Mode name

Entry

Wakeup

Effect on VCORE domain clocks

Effect on VDD

domain

clocks

Voltage regulator

Low-power run

LPSDSR and LPRUN bits + Clock setting

The regulator is forced in Main regulator (1.8 V)

None

None

In low-power mode

Sleep

(Sleep now or Sleep-on-exit)

WFI

Any interrupt

CPU CLK OFF

no effect on other clocks or analog clock sources

None

ON

WFE

Wakeup event

Low-power sleep (Sleep now or Sleep- on-exit)

LPSDSR bits + WFI

Any interrupt

CPU CLK OFF

no effect on other clocks or analog clock sources,

Flash CLK OFF

None

In low-power mode

LPSDSR bits + WFE

Wakeup event

Stop

PDDS, LPSDSR

bits + SLEEPDEEP bit + WFI or WFE

Any EXTI line (configured in the EXTI registers, internal and external lines)

All VCORE

domain clocks OFF

HSI and HSE and MSI

oscillators OFF

In low-power mode

Standby

PDDS bit + SLEEPDEEP bit + WFI or WFE

WKUP pin rising edge, RTC alarm (Alarm A or Alarm B), RTC Wakeup event, RTC tamper event, RTC timestamp event, external reset in NRST pin, IWDG reset

OFF

For Sleep and Stop (Idle and Sleep) the retention is quite simple, but if you want to use Standby (shutdown) to save data we must use the RTC register and SRAM memory.

In this scenario, you must understand how some parts interact with the system:

Voltage Regulator

The voltage regulator is always enabled after Reset and works in three different modes depending on the application modes:

STM32: internal regulator and VBAT switch

Battery Backup Domain

To retain the content of the Backup registers and supply the RTC function when VDD is turned off, VBAT pin can be connected to an optional standby voltage supplied by a battery or by another source.
The VBAT pin powers the RTC unit, the LSE oscillator, and the PC13 to PC15 IOs, allowing the RTC to operate even when the main digital supply (VDD) is turned off.

The switch to the VBAT supply is controlled by the Power Down Reset embedded in the Reset block

Backup Registers (BKP)

And can be useful to understand the different reset systems:

System Reset

Power Reset

Backup Domain Reset

Now we can start to do some tests.

Identify the reset type

So there are some reset types and for our test can be useful to understand how to recognize Them. I use the Low-Power sketch (check the relative article if you don’t understand some parts) and I add 2 functions that identify the reset source and which clocks are ready.

/**
 * I add to a simple sketch
 * that set the time to 2022-04-20 at 16:00:00 and an alarm at 16:00:10 wake-up after 10 secs
 * 2 function that identify the reset source and witch clocks are ready
  *
 * 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"
#include <STM32RTC.h>

// Pin used to trigger a wakeup
#ifndef USER_BTN
#define USER_BTN SYS_WKUP1
#endif

const int pin = USER_BTN;

/* 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 wakedUp();
void alarmMatch(void *data);
void printWakeSource();
void printClockReady();

void setup()
{
  Serial.begin(115200);
  pinMode(pin, INPUT_PULLUP);

  printWakeSource();
  printClockReady();

  __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();
  // Attach a wakeup interrupt on pin, calling repetitionsIncrease when the device is woken up
  // Last parameter (LowPowerMode) should match with the low power state used: in this example LowPower.sleep()
//  LowPower.attachInterruptWakeup(pin, wakedUp, RISING, SHUTDOWN_MODE);

  LowPower.enableWakeupFrom(&rtc, alarmMatch, &someRandomData);

  // 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, 10, 0);
  rtc.enableAlarm(rtc.MATCH_DHHMMSS);

  // 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 wakedUp() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
	Serial.println("Wake UP pin!");
}

void printClockReady() {
	Serial.println(F("--------- CLOCK Ready -------------"));
	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."));
	}
	Serial.println(F("-----------------------------------"));
}

void printWakeSource() {
	Serial.println(F("--------- RESET   Source ----------"));
	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("-----------------------------------"));
}

I advise doing this wiring so you can attach the serial monitor to the FTDI and get all Serial messages.

Here the STM32 and ST-Link V2 used in this test STM32F103C8T6 STM32F401 STM32F411 ST-Link v2 ST-Link v2 official

Here the FTDI USB to TTL CH340G - USB to TTL FT232RL


Here my multimeter Aneng SZ18

STM32: programming via ST-Link, Serial debug via FTDI, and Amperage check on a breadboard

Software reset

Now when you upload the code you get this output.

--------- RESET   Source ----------
Wake from Pin reset.
Wake from Software reset.
-----------------------------------
--------- CLOCK Ready -------------
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
-----------------------------------
Now is 20/04/22 16:00:00.944 and we set the wake at 16:10! So wait 10secs! 

Wake-up interrupt

When the device is waked up from shutdown you get:

--------- RESET   Source ----------
-----------------------------------
--------- CLOCK Ready -------------
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
LSI oscillator clock ready.
-----------------------------------
Now is 20/04/22 16:00:00.944 and we set the wake at 16:10! So wait 10secs! 

So nothing information about that.

RESET button

If you click on RESET button (reset pin) you obtain this result.

--------- RESET   Source ----------
Wake from Pin reset.
-----------------------------------
--------- CLOCK Ready -------------
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
-----------------------------------
Now is 20/04/22 16:00:00.940 and we set the wake at 16:10! So wait 10secs! 

In this case, use a reset pin but non activated via software like the previous situation (when uploading the code).

Disconnect from the power supply

Now the last interesting test is to remove the device from the power supply, the result is this.

--------- RESET   Source ----------
Wake from POR/PDR or BOR reset.
Wake from Pin reset.
Wake from POR/PDR reset.
-----------------------------------
--------- CLOCK Ready -------------
HSI oscillator clock ready.
HSE oscillator clock ready.
Main PLL clock ready.
-----------------------------------
Now is 20/04/22 16:00:00.944 and we set the wake at 16:10! So wait 10secs! 

Preserve variable value across RESET

Before starting to analyze the RTC register and SRAM, I’d like to show some systems to preserve value across RESET.

We can use the attribute __attribute__((__section__(".noinit"))); and __attribute__((__section__(".persistent"))); this simple attribute moves the stored position of the variable in a “noinit” and “persistent” section.

But you can understand the behavior with this simple sketch:

/**
 * A simple sketch to evaluate noinit and persistent variable pragma
 *
 * Renzo Mischianti <www.mischianti.org>
 * https://mischianti.org
 *
 */

unsigned boot_count __attribute__((__section__(".noinit")));
unsigned boot_count_persistent __attribute__((section(".persistent")));

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

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
      boot_count = 0;
      boot_count_persistent = 0;
      Serial.println("START: First boot!");
  } else {
	  Serial.println("START!");
  }
  __HAL_RCC_CLEAR_RESET_FLAGS();

  Serial.print("Boot number: ");
  Serial.println(boot_count);
  boot_count = boot_count + 1;

  Serial.print("Boot number persistent: ");
  Serial.println(boot_count_persistent);
  boot_count_persistent = boot_count_persistent + 1;
}

void loop()
{
  delay(1000);
}

Here It’s the serial output:

START!
Boot number: 4122089554 
Boot number persistent: 587262422
START: First boot!
Boot number: 0
Boot number persistent: 0
START!
Boot number: 1
Boot number persistent: 1
START!
Boot number: 2
Boot number persistent: 2
START!
Boot number: 3
Boot number persistent: 3
START!
Boot number: 4
Boot number persistent: 4

At the first start It loads the memory value of initialized variables, at line 4 I disconnect the power, and when I reconnect It I intercept the first boot and I reset the value of the variable.

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
      boot_count = 0;
      boot_count_persistent = 0;
      Serial.println("START: First boot!");
  } else {
	  Serial.println("START!");
  }

Now every time I click on reset (when START! the string is printed) the code shows me the current value of the variable.

But what happened if I try to use this system with a shutdown.

/**
 * A simple sketch that set the time to
 * 2022-04-20 at 16:00:00
 * and an alarm at
 * 16:00:20
 * the result is the interrupt after 10 secs
 *
 * I initialize the variables in noinit and persistent area, if
 * you click the reset button the values are persisted but on shutdown/standby
 * the values are losts
 *
 * Renzo Mischianti <www.mischianti.org>
 * https://mischianti.org
 *
 */

unsigned boot_count __attribute__((__section__(".noinit")));
unsigned boot_count_persistent __attribute__((section(".persistent")));

#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 setup()
{
  Serial.begin(115200);

  printWakeSource();

  // Initialize the variable only on first power-on reset
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) {
      boot_count = 0;
      boot_count_persistent = 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: ");
  Serial.println(boot_count);
  boot_count = boot_count + 1;

  Serial.print("Boot number persistent: ");
  Serial.println(boot_count_persistent);
  boot_count_persistent = boot_count_persistent + 1;

  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:%02d! So wait 10secs! \n",
		  rtc.getDay(), rtc.getMonth(), rtc.getYear(),
		  rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds(), wakeSeconds);
  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("-----------------------------------"));
}


The result is in this serial output:

-----------------------------------
Wake from POR/PDR or BOR reset.
Wake from Pin reset.
Wake from POR/PDR reset.
-----------------------------------
Boot number: 0
Boot number persistent: 0
Start shutdown mode in 
10 9 8 -----------------------------------
Wake from Pin reset.
-----------------------------------
Boot number: 1
Boot number persistent: 1
Start shutdown mode in 
10 9 8 7 6 5 4 3 2 1 OK!
Now is 20/04/22 16:00:10.396 and we set the wake at 16:20! So wait 10secs! 
-----------------------------------
-----------------------------------
Boot number: 3475117321
Boot number persistent: 1065079675
Start shutdown mode in 
10 9 8 7 6 

You can see that the first time I press the reset button, and then I wait for the shutdown. Shutdown power off and cut all non backup areas so you lost the value of the variable.

Thanks

  1. STM32F1 Blue-Pill: pinout, specs, and Arduino IDE configuration (STM32duino and STMicroelectronics)
  2. STM32: program (STM32F1) via USB with STM32duino bootloader
  3. STM32: programming (STM32F1 STM32F4) via USB with HID boot-loader
  4. STM32F4 Black-Pill: pinout, specs, and Arduino IDE configuration
  5. STM32: ethernet w5500 with plain HTTP and SSL (HTTPS)
  6. STM32: ethernet enc28j60 with plain HTTP and SSL (HTTPS)
  7. STM32: WiFiNINA with ESP32 WiFi Co-Processor
    1. STM32F1 Blue-pill: WiFi shield (WiFiNINA)
    2. STM32F4 Black-pill: WiFi shield (WiFiNINA)
  8. How to use SD card with stm32 and SdFat library
  9. \STM32: SPI flash memory FAT FS
  10. STM32: internal RTC, clock, and battery backup (VBAT)
  11. STM32 LoRa
    1. Unleashing IoT Potential: Integrating STM32F1 Blue-Pill with EByte LoRa E32, E22, and E220 Shields
    2. Unleashing IoT Potential: Integrating STM32F4 Black-Pill with EByte LoRa E32, E22, and E220 Shields
  1. STM32 Power saving
    1. STM32F1 Blue-Pill clock and frequency management
    2. STM32F4 Black-Pill clock and frequency management
    3. Intro and Arduino vs STM framework
    4. Library LowPower, wiring, and Idle (STM Sleep) mode
    5. Sleep, deep sleep, shutdown, and power consumption
    6. Wake up from RTC alarm and Serial
    7. Wake up from the external source
    8. Backup domain intro and variable preservation across reset
    9. RTC backup register and SRAM preservation
  2. STM32 send emails with attachments and SSL (like Gmail): w5500, enc28j60, SD, and SPI Fash

Spread the love
Exit mobile version