Low Power on STM32F0

_images/lone-warrior.jpg

One Lone Electronics Technician battles with STM32F0 Low Power Modes

Documentation

If you’re a Forth addict like me, you know that mastering unfamiliar hardware involves reading what documentation you can find, understanding it, then trying out your assumptions based on the new knowledge.

In this case, the ST documentation was incomplete, ambiguous, maybe outdated, and I certainly misunderstood some of it.

Note

Caveat, I prefer ST Documentation, it’s not perfect, but I think it’s nicely laid out and easy to read. ST had a massive job documenting Cortex M plus their peripherals, registers, bitfields etc.

Documentation Fragmentation

This was my main issue, because the key (SCR_SLEEPDEEP) to initiating ‘Standby Mode’ wasn’t obvious to me when I read the STM32F0 Technical and Programming pdfs.

For one thing, I rely heavily on CMSIS-SVD to create all the STM32F051 Memory Mapped Words and Bitfields for my code and while CMSIS-SVD has all the MCU Peripheral data I need, there is no mention of the System Control Register.

The System Control Register information was hiding in STM document PM0215 on page 81. https://www.st.com/resource/en/programming_manual/dm00051352.pdf

Researching OnLine Code Examples

Forth

To put it bluntly, there aren’t any STM32F0 low power code examples for Forth online that I can find.

Google: "forth stm32f0 low power"
No results found for "forth stm32f0 low power".

C Code Examples

These are everywhere, but usually not much use to me.

<rant>: Why I Don’t Use C Programming Language Examples

STM32F0 Low Power

One can’t just consider the Low Power Modes by themselves, because they fit in the middle of the whole process.

Something activates the Low Power process, and something wakes it up. Both the activate and wakeup process can be somewhat complex in themselves, involving the wide range of peripheral devices and interrupts.

Activating Low Power

I’ll try to keep it simple, and start the Low Power Modes using the WFI instruction on a STM32F0 Discovery Board.

WFI ( Wait For Interrupt)

Is a hint instruction that suspends execution until one of the following events occurs:

An exception
An interrupt becomes pending which would preempt if PRIMASK was clear
A Debug Entry request, regardless of whether Debug is enabled.

Note

WFI is intended for power saving only. When writing software assume that WFI might behave as a NOP operation.

WFI Forth Word

Presented below as WFI is not yet in the Mecrisp-Stellaris Dictionary.

: wfi ( -- mcu asleep ) [ $BF30 h, ] inline ;  \ WFI Opcode, Wait For Interrupt, enters

Low Power Register/Bitfields

There are three Bitfields (SLEEPDEEP, PDDS and LPDS) that control the low power modes.

Bitfield Name Description Register Memory Bit
SLEEPDEEP System Control Register SCR 0xE000ED10 2
PDDS Power down deep sleep PWR_CR 0x40007000 1
LPDS Low power deep sleep PWR_CR 0x40007000 2

Low Power Modes

X = Don't Care
All Low Power Modes are initiated by the WFI or WFE instruction
Current Voltage Clock MHz Mode SLEEPDEEP PDDS PDS REGULATOR CALL WFI?
16 mA 3.0 48 RUN X X X ON NO
3.1mA 3.0 8 RUN X X X ON NO
1.4 mA 3.0 8 SLEEP 0 X X ON YES
0.9 mA 3.0 8 STOP 1 0 0 ON YES
0.9 mA 3.0 8 STOP LOW POWER 1 1 0 LOW POWER YES
4.8 uA 3.0 8 STANDBY 1 X 1 OFF YES

Internal 1.8 Volt Regulator

The voltage regulator is always enabled after Reset. It works in five different modes.

Mode Description
Run The regulator supplies power to the core, memories and digital peripherals (everywhere).
Sleep The CPU clock is turned off, all other clocks remain running.
Stop All clocks are stopped, the regulator supplies power only to the registers and SRAM
Stop Low Power All clocks are stopped except when required by any peripheral that can work in STOP mode. Registers and SRAM have power.
Standby The regulator is powered off except for the Standby circuitry and the RTC. The contents of the registers and SRAM are lost.

Note

One ongoing mystery is which peripherals “can work in STOP mode”, and which ones cannot ? more testing is needed.

Wakeup the Device

Mode Wakeup Methods
Sleep Any interrupt can wake-up the device.
Stop The User Pushbutton via A WKUP pin
Stop Low Power The User Pushbutton via A WKUP pin, RTC
Standby The User Pushbutton via A WKUP pin (rising edge), IWDG reset, external reset on NRST pin, RTC alarm.

My Example Forth Code

Download: f0-lowpower-1.fs

\ Program Name: f0-lowpower-1.fs
\ This program may require other support files listed in preload.sh which need to be loaded first
\ Date: Mon 12 Nov 2018 11:43:22 AEDT
\ Copyright 2018  t.porter <terry@tjporter.com.au>, licensed under the GPL
\ For Mecrisp-Stellaris by Matthias Koch.
\ https://sourceforge.net/projects/mecrisp/
\ Chip: STM32F051, Board: STM32F0 Discovery Board
\ Clock: 8 Mhz using the internal STM32F051 RC clock, unless otherwise stated
\ All register names are CMSIS-SVD compliant
\ Note: gpio a,b,c,d,e, and uart1 are enabled by Mecrisp-Stellaris Core.
\
\ Purpose: To test the STM32F051 LOW Power Modes 
\
\ ---------------------------------------------------------------------------\  
 compiletoram

  \ SCR System Control Register, see PM0215 page 81 and README-f0-lowpower.txt
   $E000ED10  CONSTANT  SCR      ( System Control )   \ read/write, Reset value $00000000

   : sys-init
   ( RCC_APB1ENR_PWREN-SET )  %1  28  lshift $4002101C  bis!  \ Enable APB1 peripheral bus
   ;
   
   : green-on ( -- )  %1   9  lshift $48000818 bis! ;  \ GPIOC_BS9 Green LED ON 
   : green-off ( -- ) %1  25  lshift $48000818 bis! ;  \ GPIOC_BR9 Green LED OFF
   : wfi ( -- mcu asleep ) [ $BF30 h, ] inline ;  \ WFI Opcode, Wait For Interrupt
   : SLEEPDEEP=0 1 2 lshift SCR bic! ;	     \  SLEEPDEEP bit 3; 0 = sleep, 1 = deep sleep
   : SLEEPDEEP=1 1 2 lshift SCR bis! ;
   : PDDS=0 %1  1  lshift $40007000  bic! ;  \ PWR_CR_PDDS; Power down SLEEPDEEP
   : PDDS=1 %1  1  lshift $40007000  bis! ;
   : LPDS=0 %1  1  lshift $40007000  bic! ;  \ PWR_CR_LPDS; Low-power deep sleep
   : LPDS=1 %1  1  lshift $40007000  bis! ;


   : wakeup-config
   ( PWR_CR_CWUF-SET )   %1  2  lshift $40007000  bis!   \ PWR_CR_CWUF; Clear wakeup flag
   ( PWR_CSR_EWUP-SET )  %1  8  lshift $40007004  bis!  \ PWR_CSR_EWUP; Enable WKUP pin
   ;
  
   : userpb-interrupt-config
   ( SYSCFG_EXTICR1_EXTI0-SET ) ( %0000 -- ) %1111  0  lshift $40010008  bic!  \ x000: PA[x] pin GPIO-A Pin 0  Pin trigger
   ( NVIC_ISER_SETENA-SET )  %100000    0  lshift $E000E100  bis!  \ EXTI0_1  EXTI Line[1:0] interrupts,  it's connected to PA0 1
   ( EXTI_IMR_MR0-SET )  %1  0  lshift $40010400  bis!  \ EXTI_IMR_MR0; Interrupt request on line 0 enabled
   ( EXTI_FTSR_TR0-SET )  %1  0  lshift $4001040C  bis!  \ EXTI_FTSR_TR0; Falling trigger event line 0 when USR pbbutton is released
   ( EXTI_EMR_MR0-SET )  %1  0  lshift $40010404  bis!  \ EXTI_EMR_MR0; Event request on line 0 enabled
   ;

   : disable-pb-int ( EXTI_IMR_MR0-RESET )  %1  0  lshift $40010400  bic! ; \ EXTI_IMR_MR0; Interrupt request on line 0 disabled

   : pb-interrupt-handler 
   ( EXTI_PR_PR0-SET )  %1  0  lshift $40010414  bis!  \ EXTI_PR_PR0; Pending bit 0, clear 
   100 0 do nop loop		 \ delay before sending message after restart
   cr ." System awake ... " cr 
   green-on
   disable-pb-int
   ;
   
   : pb-interrupt-hook  
   ['] pb-interrupt-handler irq-exti0_1 ! \ tie the interrupt handler to the interrupt
   ;


\ All modes, many are redundant, left in for completeness, try them!
 
   : SLEEP cr	\ 1.7 mA  Repeat without reboot.
    ." Entering SLEEP MODE: CPU clock off, all peripherals are running " cr 
    ." Press the blue USER push button to wake up ..... " cr
      userpb-interrupt-config
      SLEEPDEEP=0
      PDDS=0
      LPDS=0
      green-off
      wfi
   ;


   : SLEEP-1 cr	 ( redundant) \ 1.7 mA  Repeat without reboot.
    ." Entering SLEEP MODE: CPU clock off, all peripherals are running " cr 
    ." Press the blue USER push button to wake up ..... " cr
      userpb-interrupt-config
      SLEEPDEEP=0
      PDDS=0
      LPDS=1
      green-off
      wfi
   ;

   : SLEEP-2 cr	( redundant) \ 1.7 mA  Repeat without reboot.
    ." Entering SLEEP MODE: CPU clock off, all peripherals are running " cr 
    ." Press the blue USER push button to wake up ..... " cr
      userpb-interrupt-config
      SLEEPDEEP=0
      PDDS=1
      LPDS=0
      green-off
      wfi
   ;

   : SLEEP-3 cr	( redundant) \ 1.7 mA Repeat without reboot.
    ." Entering SLEEP MODE: CPU clock off, all peripherals are running " cr 
    ." Press the blue USER push button to wake up ..... " cr
      userpb-interrupt-config
      SLEEPDEEP=0
      PDDS=1
      LPDS=1
      green-off
      wfi
   ;

   
   : STOP cr   \ 0.802 mA  Repeat without reboot
   ." Press the blue USER push button to wake up ..... " cr
   ." All clocks are stopped, the regulator supplies power only to the registers and SRAM " cr
      userpb-interrupt-config
      wakeup-config
      SLEEPDEEP=1
      PDDS=0
      LPDS=0
      green-off
      wfi
   ;

    : STOP-LOW-POWER cr      \ 0.802 mA   Repeat without reboot.
   ." All clocks are stopped except when required by any peripheral that can work in STOP mode. Registers and SRAM have power. " cr
   ." Press the blue USER push button to wake up " cr
      userpb-interrupt-config
      wakeup-config
      SLEEPDEEP=1
      PDDS=1
      LPDS=0
      green-off
      wfi
   ;

   : STANDBY cr      \ 4 uA . Reboots
   ." Press the blue USER push button to wake up " cr
   ." This will REBOOT the MCU, all RAM will be lost " cr
      userpb-interrupt-config
      wakeup-config
      SLEEPDEEP=1
      PDDS=0
      LPDS=1
      green-off
      wfi
   ;

   : STANDBY-1 cr ( redundant)  \ 4 uA . Reboots
   ." Press the blue USER push button to wake up ..... " cr
      userpb-interrupt-config
      wakeup-config
      SLEEPDEEP=1
      PDDS=1
      LPDS=1
      green-off
      wfi
   ;

      
   : main			\ my multitasking, harmless to leave it in
     pause
   ;

 
   : init-general		 
    sys-init
    pb-interrupt-hook
    green-on
   ;


   \ init-general		 \ uncomment to run on your system