Page 1 of 1

Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 12:03 pm
by goeck
Hey everyone,

I want to lay my STM32F031 asleep (stop mode) after a period of time where nothing happens. So I used a virtual timer for this task, which works quite well (timer and setting asleep). But I can't wake up from EXTI source anymore (active high button at PA=). My assumtption is, that this is due to the callback of the timer where the EXTI channel should be enabled, but doesn't. I get the wake up done, when I call the callback directly (without invloving a VT) and just wait some time using chThdSleepSeconds(2); prior to calling the callback. :?:

So I use the following code:

Code: Select all

VirtualTimer vtGoToSleep;
 
void goToSleep(){
   chSysLockFromIsr();

   // Enable wake up interrupt.
   extChannelEnableI(&EXTD1, 0);

   // Look deep into my eyes, you are tired now ...
   PWR->CR |= (PWR_CR_LPSDSR | PWR_CR_CSBF | PWR_CR_CWUF);               // stop mode
   PWR->CR &= ~PWR_CR_PDDS;
   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
   __WFI();

   chSysUnlockFromIsr();
}

static void wakeUpIRQ(EXTDriver *extp, expchannel_t channel) {
   (void)extp;
   (void)channel;

   chSysLockFromIsr();

   // We must reinit clocks after waking up ESPECIALLY if use HSE or HSI+PLL.
   stm32_clock_init();

   // Since we are awake now, no need to let the wakeUp interrupt be enabled.
   extChannelDisableI(&EXTD1, 0);

   chSysUnlockFromIsr();
}
 
static const EXTConfig extcfg = {
  {
   /* Pin  0 */ {EXT_CH_MODE_RISING_EDGE | EXT_MODE_GPIOA, wakeUpIRQ},
   /* Pin  1 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  2 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  3 */ {EXT_CH_MODE_FALLING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, buttonIRQ},
   /* Pin  4 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  5 */ {EXT_CH_MODE_FALLING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, buttonIRQ},
   /* Pin  6 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  7 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  8 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin  9 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 10 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 11 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 12 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 13 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 14 */ {EXT_CH_MODE_DISABLED, NULL},
   /* Pin 15 */ {EXT_CH_MODE_DISABLED, NULL}
  }
};

int main(int argc, char **argv) {
   chVTSet(&vtGoToSleep, S2ST(2), goToSleep, NULL);

   while(TRUE){
      chThdSleepMilliseconds(1000);
      palTogglePad(LED_PORT, LED_PIN)
   }
}

Thanks for the quick help. I appreceate it.
Stefan

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 12:18 pm
by Giovanni
Hi,

Virtual timers are software timers, if the system is stopped those cannot continue to count time. I think you need a physical timer for this or the RTC depending on the deep sleep state.

Giovanni

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 12:50 pm
by goeck
Hi Giovanni, thanks for the reply.
Oh Well, maybe there was a misunderstanding. The timer is just ment to fire the lay-asleep-process delayed. This actually works, but not the wake up, which is just a configured EXTI line (channel 0). It seems, the configuration is not properly done, but I don't really know what is done wrong. So in other words, I think after the WFI was called no timer is involved anymore since the timer callback has been successfully called.
I don't want to wake up the MCU using a timer, I jjust want to set it asleep using the timer. Wake up should be done by pressing the button at PA0.

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 1:44 pm
by Giovanni
That is something I never tried first-hand, if I remember well barthess worked with sleep modes for a while, you could try asking him via PM or on IRC.

I am not sure you can get an EXT callback after waking up too, doesn't the system restart via the reset vector? if so you need to use external "events" not interrupts (no callback in the EXT driver).

Giovanni

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 1:46 pm
by goeck
The strange thing is, If I go without the usage of the virtual timer all of that works, only if the goToSleep() gets called by the timer the MCU doesn't wake up. So to me, it seems like the WFI (instead of WFE) might be the right choice. Reset like behaviour only occirs after getting up from standby mode, I use stop mode.

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 1:50 pm
by Giovanni
If you call WFI while interrupts are disabled then you will never get out.

chSysLock/Unlock disable interrupts.

Giovanni

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 2:49 pm
by goeck
OK, yeah...I think I've got it. So I can't actually call __WFI() from any interrupt driven callback because they are executed when interrupts are disabled , right?
What I've done now, is an expensive approach, but it works.
I've setup another waiting thread that simply has the code to set the MCU asleep.

Code: Select all

static msg_t Thread1(void *param) {
   (void)param;

   // Wait for signal
   chEvtWaitAny((eventmask_t)2);

    while (TRUE) {
      // Enable wake up interrupt.
      extChannelEnable(&EXTD1, 0);

      // Look deep into my eyes, you are tired now ...
      PWR->CR |= (PWR_CR_LPSDSR | PWR_CR_CSBF | PWR_CR_CWUF);               // stop mode
      PWR->CR &= ~PWR_CR_PDDS;
      SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
      __WFI();
    }
}

The goToSleep() looks like this now:

Code: Select all

void goToSleep(){
   if(tpSleep)
      chEvtSignalI(tpSleep, (eventmask_t)2);
}

And the usage of a virtual timer degrades to this

Code: Select all

chVTSet(&vtGoToSleep, S2ST(SLEEP_TIME), goToSleep, NULL);

Actually, I was now asking myself, how this WaitForEvents works, maybe this should be the way to go then. I just know, it doesn't work right away. As the Refernce Manual says, I have to configure EXTI for event mode rather than for interrupt mode... :?:
Mode exit:

If WFI was used for entry:
– Any EXTI Line configured in Interrupt mode (the corresponding EXTI
Interrupt vector must be enabled in the NVIC).
[...]

If WFE was used for entry:
Any EXTI Line configured in event mode.

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 2:55 pm
by Giovanni
Simply don't specify a callback in the EXT driver configuration (NULL), the channel will be configured as an event instead of an interrupt.

Giovanni

Re: Use of a Virtual Timer to set MCU asleep

Posted: Thu Jan 30, 2014 7:02 pm
by goeck
Oh thanks, Giovanni. Somehow in the first run I couldn't get it working using events (__WFE()) though I followed your advice on this topic. I was missing out on some prerequisites, thus the MCU wouln't go into standby but just keep running.
So for now I stuck with WFI and further decreased code complexity. I put the shut down code into main while loop and added a chThdSleep() in front. All the other related construtcs (dedicated thread, virtual timer, callback function) are deprecated and have been deleted.
So I am a bit happy now with this, but still I'd like to overcome the WFE thing...