Site icon Renzo Mischianti

STM32 Risparmio energetico: introduzione al dominio di backup e conservazione delle variabili durante il RESET – 8

Risparmio energetico STM32: introduzione del dominio di backup e conservazione delle variabili durante il ripristino

Risparmio energetico STM32: introduzione del dominio di backup e conservazione delle variabili durante il ripristino

Spread the love

STM32 risparmio energetico: introduzione al dominio di backup e conservazione delle variabili dopo il reset

Un elemento importante degli STM32 è il dominio di backup, ma dopo una breve introduzione valuteremo e testeremo la soluzione standard per il RESET, praticamente l’uso delle variabili nelle aree di memoria “noinit” e “persistent”, funzionalità molto interessanti. E per un uso specifico, scriveremo alcune semplici funzioni per verificare le caratteristiche dei nostri dispositivi.

Variabili, registro RTC e conservazione SRAM

Una delle cose più utili di cui potresti aver bisogno quando utilizzi le funzioni a basso consumo energetico è la conservazione dello stato, e prima di tutto faremo un veloce riassunto della principale modalità Low-Power.

I dispositivi presentano quattro principali modalità a basso consumo:

  • Modalità VBAT:
    • Questa modalità viene utilizzata solo quando l’alimentazione digitale principale (VDD) è spenta.
    • Il circuito è alimentato attraverso il pin VBAT che dovrebbe essere collegato a una tensione di alimentazione esterna (una batteria o qualsiasi altra fonte).
    • Il pin VBAT alimenta il dominio di backup (registri RTC, registro di backup RTC e SRAM di backup)
  • Nome modalità

    Ingresso

    Risveglio

    Effetto sugli orologi del dominio VCORE

    Effetto sugli orologi del dominio VDD

    Regolatore di tensione

    Funzionamento a basso consumo

    Bit LPSDSR e LPRUN + Impostazione orologio

    Il regolatore è forzato nel regolatore principale (1,8 V)

    Nessuno

    Nessuno

    In modalità a basso consumo

    Sleep

    (Sleep immediato o Sleep-all’uscita)

    WFI

    Qualsiasi interruzione

    CPU CLK OFF

    nessun effetto sugli altri orologi o sorgenti di orologio analogiche

    Nessuno

    ON

    WFE

    Evento di risveglio

    Low-power sleep (Sleep immediato o Sleep-all’uscita)

    Bit LPSDSR + WFI

    Qualsiasi interruzione

    CPU CLK OFF

    nessun effetto sugli altri orologi o sorgenti di orologio analogiche,

    Flash CLK OFF

    Nessuno

    In modalità a basso consumo

    Bit LPSDSR + WFE

    Evento di risveglio

    Stop

    Bit PDDS, LPSDSR

    + bit SLEEPDEEP + WFI o WFE

    Qualsiasi linea EXTI (configurata nei registri EXTI, linee interne ed esterne)

    Tutti gli orologi del dominio VCORE OFF

    Oscillatori HSI, HSE e MSI OFF

    In modalità a basso consumo

    Standby

    Bit PDDS + bit SLEEPDEEP + WFI o WFE

    Rising edge del pin WKUP, allarme RTC (Allarme A o Allarme B), evento di risveglio RTC, evento tamper RTC, evento timestamp RTC, reset esterno nel pin NRST, reset IWDG

    OFF

    Per le modalità Sleep e Stop (Idle e Sleep) la ritenzione è abbastanza semplice, ma se si desidera utilizzare la modalità Standby (spegnimento) per salvare i dati, bisogna utilizzare il registro RTC e la memoria SRAM.

    In questo scenario, è necessario comprendere come alcune parti interagiscono con il sistema:

    Regolatore di Tensione

    Il regolatore di tensione è sempre abilitato dopo il Reset e funziona in tre diverse modalità a seconda delle modalità dell’applicazione:

    STM32: regolatore interno e interruttore VBAT

    Dominio di Backup della Batteria

    Per conservare il contenuto dei registri di backup e alimentare la funzione RTC quando VDD è spento, il pin VBAT può essere collegato a una tensione di standby opzionale fornita da una batteria o da un’altra fonte.
    Il pin VBAT alimenta l’unità RTC, l’oscillatore LSE e gli IO PC13 a PC15, permettendo al RTC di funzionare anche quando l’alimentazione digitale principale (VDD) è spenta.

    Il passaggio all’alimentazione VBAT è controllato dal Reset di Spegnimento incorporato nel blocco di Reset

    Registri di Backup (BKP)

    E può essere utile comprendere i diversi sistemi di reset:

    Reset di Sistema

    Reset di Alimentazione

    Reset del Dominio di Backup

    Ora possiamo iniziare a fare alcuni test.

    Identificare il tipo di reset

    Quindi, ci sono alcuni tipi di reset e per il nostro test può essere utile capire come riconoscerli. Uso lo sketch di basso consumo energetico (consulta l’articolo relativo se non capisci alcune parti) e aggiungo 2 funzioni che identificano la fonte del reset e quali orologi sono pronti.

    /**
     * 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("-----------------------------------"));
    }
    

    Consiglio di effettuare questo cablaggio in modo da poter collegare il monitor seriale all’FTDI e ricevere tutti i messaggi Seriali.

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

    Ecco l'FTDI USB to TTL CH340G - USB to TTL FT232RL


    Ecco il mio multimetro Aneng SZ18

    STM32: programmazione tramite ST-Link, debug seriale tramite FTDI e controllo dell’amperaggio su breadboard

    Reset software

    Ora, quando carichi il codice, ottieni questa uscita:

    --------- 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! 
    

    Interrupt di risveglio

    Quando il dispositivo viene risvegliato dallo spegnimento, ottieni:

    --------- 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! 
    

    Quindi nessuna informazione a riguardo.

    Pulsante RESET

    Se fai clic sul pulsante RESET (pin di reset), ottieni questo risultato.

    --------- 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 questo caso, usa un pin di reset ma non attivato tramite software come nella situazione precedente (quando si carica il codice).

    Scollegare dall’alimentazione

    Ora l’ultimo test interessante è rimuovere il dispositivo dall’alimentazione, il risultato è questo.

    --------- 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! 
    

    Preservare il valore delle variabili attraverso il RESET

    Prima di iniziare ad analizzare il registro RTC e la SRAM, vorrei mostrare alcuni sistemi per preservare i valori attraverso il RESET.

    Possiamo usare l’attributo __attribute__((__section__(".noinit"))); e __attribute__((__section__(".persistent"))); Questo semplice attributo sposta la posizione di archiviazione della variabile in una sezione “noinit” e “persistent”.

    Ma puoi capire il comportamento con questo semplice 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
    

    Alla prima avviata, carica il valore in memoria delle variabili inizializzate, alla riga 4 scollego l’alimentazione e quando la riconnetto intercetto il primo avvio e resetto il valore della variabile.

      // 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!");
      }
    

    Ora, ogni volta che clicco sul reset (quando viene stampata la stringa START!), il codice mi mostra il valore corrente della variabile.

    Ma cosa succede se provo a utilizzare questo sistema con uno spegnimento?

    /**
     * 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("-----------------------------------"));
    }
    
    
    

    Il risultato si trova in questa uscita seriale:

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

    Puoi vedere che la prima volta premo il pulsante di reset e poi aspetto lo spegnimento. Lo spegnimento spegne e interrompe tutte le aree non di backup, quindi perdi il valore della variabile.

    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
    2. STM32 invia email con allegati e SSL (come Gmail): w5500, enc28j60, SD e SPI Flash

    Spread the love
    Exit mobile version