How to put spark core to sleep and wakeup on interrupt signal on a pin?

Thanks @ScruffR for the help. I will assume the wakeup by pin in deep sleep will not work.

Suggested Improvement: A statement on the docs.spark.io/firmware page to that effect would be a good help to other users.

1.) Using sleep or deep sleep, you will still be able to trigger a wake up via a pin

2.) Using the bootloader patch, it works like a normal user firmware that you flash so there’s not harm to patch it if you need.

But that’s for really early cores with old bootloader though…

@kennethlimcp, how do you set this up?

I just can't find a Spark.sleep() overload either that takes a SLEEP_MODE_DEEP and a wakeUpPin/edgeTriggerMode combination in spark_wiring_utils.cpp.

And the STM32F docs state:

° Standby mode
The Standby mode is used to achieve the lowest power consumption. The internal voltage regulator is switched off so that the entire 1.8 V domain is powered off. The PLL, the HSI RC and the HSE crystal oscillators are also switched off. After entering Standby mode, SRAM and register contents are lost except for registers in the Backup domain and Standby circuitry.
The device exits Standby mode when an external reset (NRST pin), an IWDG reset, a rising edge on the WKUP pin, or an RTC alarm occurs.

If you were refering to the RST pin to wake the Core, then I can agree but without the GPIOs powered in "Stand-by mode" (deep sleep) I can't see how this should work.

1 Like

Ah indeed… So probably that’s not possible for pin trigger during deep sleep?

Good point, just some minor problem if you extrapolate the thought.
If you'd want everything documented that will not be possible you'll have work for life and will still not have thought of every possible other impossibility.

The other way round, if you only document what's possible you only got a finite job to do, leaving some time to answer questions why some things that apparently should work but are not documented actually don't work :wink:

For this paticular point, it wasn't documented because it doesn't work - or since it wasn't documented you could assume it's most likely not possible.

Thanks @kennethlimcp and @ScruffR for your help.

@ScruffR I take your point that there is an infinite list of things what will not work, but clearly there are many users of the core that are trying to use it in a low power situation. I have never found more clarity in documentation to be a bad thing, so my suggestion still stands.

There is a wakeup pin on STM32 - PA0 (I think A0 on Core) which can wakeup STANDBY mode(SLEEP_MODE_DEEP) with a rising edge on the WKUP pin - according to STM32 datasheet.

@HardWater, yes you can reflash the bootloader without needing a JTAG programmer.

1 Like

Thanks @satishgn for this input :+1:

I’ve just added this to the docs and filed a pull request for it.

@HardWater, once this pull request gets merged into the docs, could you check if this makes it any clearer?


BTW: Being open source anybody can add info to the docs and file pull requests, just by tapping the Edit this page button in the top right of the page.

@ScruffR, did you managed to test that it works?

@ScruffR , @satishgn Thanks for the followup. I am trying to make it work but no luck so far. Here is a snippet of code that I am working with. Code comes from ST’s site

All that is happening is it sleeps for 2 minutes then awakens

Any comments or ideas would be most helpful.

\#include "stm32f10x.h"
SYSTEM_MODE(MANUAL);

void setup()
   {
   pinMode(A0, INPUT); //I have a 10k pull down    
  }

void loop()
   {
   flash some leds here

   /* Enable PWR and BKP clock */
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

   /* Enable WKUP pin */
   PWR_WakeUpPinCmd(ENABLE);

   /* Allow access to BKP Domain */
   PWR_BackupAccessCmd(ENABLE);

   /* Configure RTC clock source and prescaler */
   RTC_Configuration();

   /* Configure the SysTick to generate an interrupt each 250 ms */
   SysTick_Configuration();

   //Spark.sleep(sleep_delay);
   Spark.sleep(SLEEP_MODE_DEEP, 120); //2 minutes
   }

It looks like alot of what I have above is not necessary as I grab info from the web. I only think the following are required but still not having any luck with testing multiple versions

pinMode(A0, INPUT); //I have a 10k pull down  
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR , ENABLE);
PWR_WakeUpPinCmd(ENABLE);
PWR_ClearFlag(PWR_FLAG_SB);

Then go to deep sleep

@HardWater, I think the problem is with calling Spark.sleep() which causes a reboot of the Core when in deep sleep. It is only after the reset that Enter_STOP_Mode() gets called. This function is not a ā€œpublicā€ function but here is the code it runs:


void Enter_STOP_Mode(void)
{
  if((BKP_ReadBackupRegister(BKP_DR9) >> 12) == 0xA)
  {
    uint16_t wakeUpPin = BKP_ReadBackupRegister(BKP_DR9) & 0xFF;
    InterruptMode edgeTriggerMode = (InterruptMode)((BKP_ReadBackupRegister(BKP_DR9) >> 8) & 0x0F);

    /* Clear Stop mode system flag */
    BKP_WriteBackupRegister(BKP_DR9, 0xFFFF);

    if ((wakeUpPin < TOTAL_PINS) && (edgeTriggerMode <= FALLING))
    {
      PinMode wakeUpPinMode = INPUT;

      /* Set required pinMode based on edgeTriggerMode */
      switch(edgeTriggerMode)
      {
        case CHANGE:
          wakeUpPinMode = INPUT;
          break;

        case RISING:
          wakeUpPinMode = INPUT_PULLDOWN;
          break;

        case FALLING:
          wakeUpPinMode = INPUT_PULLUP;
          break;
      }
      pinMode(wakeUpPin, wakeUpPinMode);

      /* Configure EXTI Interrupt : wake-up from stop mode using pin interrupt */
      attachInterrupt(wakeUpPin, NULL, edgeTriggerMode);

      /* Request to enter STOP mode with regulator in low power mode */
      PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);

      /* At this stage the system has resumed from STOP mode */
      /* Enable HSE, PLL and select PLL as system clock source after wake-up from STOP */

      /* Enable HSE */
      RCC_HSEConfig(RCC_HSE_ON);

      /* Wait till HSE is ready */
      if(RCC_WaitForHSEStartUp() != SUCCESS)
      {
        /* If HSE startup fails try to recover by system reset */
        NVIC_SystemReset();
      }

      /* Enable PLL */
      RCC_PLLCmd(ENABLE);

      /* Wait till PLL is ready */
      while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

      /* Select PLL as system clock source */
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

      /* Wait till PLL is used as system clock source */
      while(RCC_GetSYSCLKSource() != 0x08);
    }
  }
}

There is pre and post STOP mode code that needs to be run, as you can see. Hopefully, this helps. :smile:

1 Like

Hi @peekay123 thanks for the response I thought that the deep sleep mode puts the core in standby mode not stop mode. I tried to find the code for Enter_STANDBY_mode() but could not find it. I will try and understand what you are saying about stop mode. Thanks

@HardWater, the Core will enter a either STOP or STANDBY modes depending on whether it needs to wake from a pin event or not. In the STM32 standby mode, only a timer event can wake the core. In stop mode, any interrupt event, including the system timer, can wake the core BUT power consumption is higher.

In the existing firmware master, in either mode a Backup Register flag is set to indicate the mode to go into AFTER a forced system reset is done.

A true STOP mode would simply stop the running code when called and wait for a wakeup event and then continue execution. I believe there may be a reason @satishgn did it this way but I can’t remember why!

Hi @peekay123 if you read the thread further up you will see that @satishgn thinks wakeup via A0 will breakout of deep sleep mode. There are lots of comments from others using the stm32f103 on the net that it works.

Are what you saying is, that the spark sleep functions do something additional to registers etc compared to ST code and that what is making the enabling and use of the wkup_pin not work?

@HardWater, what I am saying is that from what I see in the firmware, the STOP code is only triggered after a reset. Once in STOP mode, the Core can be awakened with a pin event. Problem is, wake up pin or not, the Core still resets in there instead of just stopping running user code, waking and continuing.

I believe a modified version of the Enter_STOP_Mode() could work to create an ā€œinlineā€ stop.

Hi @peekay123, @satishgn @ScruffR & @kennethlimcp .

I have the wakeup by A0 (WKUP_PIN) in Deep Sleep kind of working!!! It is a little flaky but is clearly exiting DEEP SLEEP immediately on the WKUP_Pin rising. The key to making it work was to replace spark.sleep() function with a modified copy my_sleep() and the only change in that function being was I swapped out the Spark function Enter_STANDBY_Mode(); with the ST function PWR_EnterSTANDBYMode(); What needs to be resolved ie a little flaky is it seem not exit deep sleep properly by the timer (I must not be setting it up properly. Any ideas please let me know.

Oh there is an extremely good chance that some of register settings etc before sleep are not needed or somewhat wrong. I have to play with that some more.

@satishgn Where can I get some detail on what exactly Enter_STANDBY_Mode() does and a comparison with ST’s function?

\#include "stm32f10x.h"
SYSTEM_MODE(MANUAL);

void setup()
   {
   pinMode(A0, INPUT); //I have a 10k pull down  
  }

void loop()
   {
   switch some leds here

   PWR_WakeUpPinCmd(DISABLE);
   delay(100);
   PWR_ClearFlag(PWR_FLAG_SB);
   delay(100);
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
   PWR->CR |= 0x00000100;
   PWR_WakeUpPinCmd(ENABLE);
   my_sleep(SLEEP_MODE_DEEP, sleep_delay);   
   }


void my_sleep(Spark_Sleep_TypeDef sleepMode, long seconds)
   {
   /* Set the RTC Alarm */
   RTC_SetAlarm(RTC_GetCounter() + (uint32_t)seconds);

   /* Wait until last write operation on RTC registers has finished */
   RTC_WaitForLastTask();

   switch(sleepMode)
      {
      case SLEEP_MODE_WLAN:
         WiFi.off();
         break;
      case SLEEP_MODE_DEEP:
         blink_led(RED,20,FASTLED);  //take out later to save battery
         PWR_EnterSTANDBYMode();
         blink_led(GREEN,20,FASTLED);  //take out later to save battery
         break;
      }
  }

@HardWater, from above, I think you need this code after your sleep code but only when STOP is done:

  /* Enable HSE, PLL and select PLL as system clock source after wake-up from STOP */
    
  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  if(RCC_WaitForHSEStartUp() != SUCCESS)
  {
    /* If HSE startup fails try to recover by system reset */
    NVIC_SystemReset();
  }

  /* Enable PLL */
  RCC_PLLCmd(ENABLE);

  /* Wait till PLL is ready */
  while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

  /* Select PLL as system clock source */
  RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

  /* Wait till PLL is used as system clock source */
  while(RCC_GetSYSCLKSource() != 0x08);
}
3 Likes

Okay I’ll try that tomorrow I will execute the section only if reset was caused by timer expiration and not for pin wakeup as that seems to work properly without. @peekay123 Thanks,

Hi All @peekay123, @satishgn @ScruffR & @kennethlimcp,

I have not been able to get this working properly only partially but thought I would clarify the results if someone wanted to work on it further. Or maybe I will pick it up again at some point.

The code definitely breaks out of Deep Sleep mode on the rise of pin A0.
Then runs the user code as normal.

The problems are:

  1. The switching of A0 to high must occur within the first 3 seconds (approx) of going to sleep otherwise it fails.
  2. Wakeup from sleep by timer no longer works properly.

To me the easiest path to getting this working properly is to have a clear understanding of the differences between Spark’s function Enter_STANDBY_Mode() and with the ST’s function PWR_EnterSTANDBYMode() maybe someone at Spark could help with that.

#include "stm32f10x.h"
SYSTEM_MODE(MANUAL);  //this is the only mode I have been testing in

void setup()
   {
   // need a pull down or probably INPUT_PULLDOWN would work
   pinMode(A0, INPUT);
   }

void my_sleep(Spark_Sleep_TypeDef sleepMode, long seconds)
   {
   /* Set the RTC Alarm */
   RTC_SetAlarm(RTC_GetCounter() + (uint32_t)seconds);

   /* Wait until last write operation on RTC registers has finished */
   RTC_WaitForLastTask();

   switch(sleepMode)
      {
      case SLEEP_MODE_WLAN:
         WiFi.off();
         break;

      case SLEEP_MODE_DEEP:
         blink_led(RED,20,FASTLED);  //take out later to save battery
         PWR_EnterSTANDBYMode();
         //never gets here
         break;
      }
   }


void loop()
   {
   // do something useful here 
 
   // get ready to sleep
   PWR_WakeUpPinCmd(ENABLE);
   my_sleep(SLEEP_MODE_DEEP, 120);  //sleep no more than 2 minutes
   }
1 Like

@HardWater, now this is getting way to serious! :stuck_out_tongue: I will have to dig into the firmware to figure that one out. :smile:

2 Likes