6767#define WAKEUP_TIME_LPDS (LPDS_UP_TIME + LPDS_DOWN_TIME + USER_OFFSET) // 20 msec
6868#define WAKEUP_TIME_HIB (32768) // 1 s
6969
70+ #define FORCED_TIMER_INTERRUPT_MS (1)
71+ #define FAILED_SLEEP_DELAY_MS (FORCED_TIMER_INTERRUPT_MS * 3)
72+
7073/******************************************************************************
7174 DECLARE PRIVATE TYPES
7275 ******************************************************************************/
@@ -110,6 +113,7 @@ typedef struct {
110113 mp_obj_t wlan_lpds_wake_cb ;
111114 mp_obj_t timer_lpds_wake_cb ;
112115 mp_obj_t gpio_lpds_wake_cb ;
116+ uint timer_wake_pwrmode ;
113117} pybsleep_wake_cb_t ;
114118
115119/******************************************************************************
@@ -131,6 +135,8 @@ void pybsleep_suspend_exit (void);
131135STATIC void pybsleep_obj_wakeup (void );
132136STATIC void PRCMInterruptHandler (void );
133137STATIC void pybsleep_iopark (void );
138+ STATIC bool setup_timer_lpds_wake (void );
139+ STATIC bool setup_timer_hibernate_wake (void );
134140
135141/******************************************************************************
136142 DEFINE PUBLIC FUNCTIONS
@@ -151,8 +157,8 @@ void pybsleep_init0 (void) {
151157 // disable all LPDS and hibernate wake up sources (WLAN is disabed/enabled before entering LDPS mode)
152158 MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_GPIO );
153159 MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_TIMER );
154- MAP_PRCMHibernateWakeupSourceDisable (PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 |
155- PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26 );
160+ MAP_PRCMHibernateWakeupSourceDisable (PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 |
161+ PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26 );
156162
157163 // store the reset casue (if it's soft reset, leave it as it is)
158164 if (pybsleep_reset_cause != PYB_SLP_SOFT_RESET ) {
@@ -216,6 +222,10 @@ void pybsleep_set_timer_lpds_callback (mp_obj_t cb_obj) {
216222 pybsleep_wake_cb .timer_lpds_wake_cb = cb_obj ;
217223}
218224
225+ void pybsleep_configure_timer_wakeup (uint pwrmode ) {
226+ pybsleep_wake_cb .timer_wake_pwrmode = pwrmode ;
227+ }
228+
219229/******************************************************************************
220230 DEFINE PRIVATE FUNCTIONS
221231 ******************************************************************************/
@@ -405,6 +415,9 @@ STATIC void PRCMInterruptHandler (void) {
405415 }
406416 break ;
407417 case PRCM_LPDS_TIMER :
418+ // disable timer was wake-up source
419+ pybsleep_wake_cb .timer_wake_pwrmode &= ~PYB_PWR_MODE_LPDS ;
420+ MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_TIMER );
408421 if (pybsleep_wake_cb .timer_lpds_wake_cb ) {
409422 mpcallback_handler (pybsleep_wake_cb .timer_lpds_wake_cb );
410423 }
@@ -466,6 +479,77 @@ STATIC void pybsleep_iopark (void) {
466479 HWREG (0x4402E10C ) = 0x00000E61 ;
467480}
468481
482+ STATIC bool setup_timer_lpds_wake (void ) {
483+ uint64_t t_match , t_curr , t_remaining ;
484+
485+ // get the time remaining for the RTC timer to expire
486+ t_match = MAP_PRCMSlowClkCtrMatchGet ();
487+ t_curr = MAP_PRCMSlowClkCtrGet ();
488+
489+ if (t_match > t_curr ) {
490+ // get the time remaining in terms of slow clocks
491+ t_remaining = (t_match - t_curr );
492+ if (t_remaining > WAKEUP_TIME_LPDS ) {
493+ // subtract the time it takes for wakeup from lpds
494+ t_remaining -= WAKEUP_TIME_LPDS ;
495+ t_remaining = (t_remaining > 0xFFFFFFFF ) ? 0xFFFFFFFF : t_remaining ;
496+ // setup the LPDS wake time
497+ MAP_PRCMLPDSIntervalSet ((uint32_t )t_remaining );
498+ // enable the wake source
499+ MAP_PRCMLPDSWakeupSourceEnable (PRCM_LPDS_TIMER );
500+ return true;
501+ }
502+ }
503+ else {
504+ // setup a timer interrupt immediately
505+ MAP_PRCMRTCMatchSet (0 , FORCED_TIMER_INTERRUPT_MS );
506+ }
507+
508+ // disable the timer as wake source
509+ MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_TIMER );
510+
511+ // LPDS wake by timer was not possible, force
512+ // an interrupt in active mode instead
513+ MAP_PRCMIntEnable (PRCM_INT_SLOW_CLK_CTR );
514+
515+ return false;
516+ }
517+
518+ STATIC bool setup_timer_hibernate_wake (void ) {
519+ uint64_t t_match , t_curr , t_remaining ;
520+
521+ // get the time remaining for the RTC timer to expire
522+ t_match = MAP_PRCMSlowClkCtrMatchGet ();
523+ t_curr = MAP_PRCMSlowClkCtrGet ();
524+
525+ if (t_match > t_curr ) {
526+ // get the time remaining in terms of slow clocks
527+ t_remaining = (t_match - t_curr );
528+ if (t_remaining > WAKEUP_TIME_HIB ) {
529+ // subtract the time it takes for wakeup from hibernate
530+ t_remaining -= WAKEUP_TIME_HIB ;
531+ // setup the LPDS wake time
532+ MAP_PRCMHibernateIntervalSet ((uint32_t )t_remaining );
533+ // enable the wake source
534+ MAP_PRCMHibernateWakeupSourceEnable (PRCM_HIB_SLOW_CLK_CTR );
535+ return true;
536+ }
537+ }
538+ else {
539+ // setup a timer interrupt immediately
540+ MAP_PRCMRTCMatchSet (0 , FORCED_TIMER_INTERRUPT_MS );
541+ }
542+
543+ // disable the timer as wake source
544+ MAP_PRCMLPDSWakeupSourceDisable (PRCM_HIB_SLOW_CLK_CTR );
545+
546+ // hibernate wake by timer was not possible, force
547+ // an interrupt in active mode instead
548+ MAP_PRCMIntEnable (PRCM_INT_SLOW_CLK_CTR );
549+
550+ return false;
551+ }
552+
469553/******************************************************************************/
470554// Micro Python bindings; Sleep class
471555
@@ -483,6 +567,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_idle_obj, pyb_sleep_idle);
483567STATIC mp_obj_t pyb_sleep_suspend (mp_obj_t self_in ) {
484568 nlr_buf_t nlr ;
485569
570+ // check if we should enable timer wake-up
571+ if (pybsleep_wake_cb .timer_wake_pwrmode & PYB_PWR_MODE_LPDS ) {
572+ if (!setup_timer_lpds_wake ()) {
573+ // lpds entering is not possible, wait for the forced interrupt and return
574+ pybsleep_wake_cb .timer_wake_pwrmode &= ~PYB_PWR_MODE_LPDS ;
575+ HAL_Delay (FAILED_SLEEP_DELAY_MS );
576+ return mp_const_none ;
577+ }
578+ }
579+
486580 // check if we need to enable network wake-up
487581 if (pybsleep_wake_cb .wlan_lpds_wake_cb ) {
488582 MAP_PRCMLPDSWakeupSourceEnable (PRCM_LPDS_HOST_IRQ );
@@ -498,6 +592,7 @@ STATIC mp_obj_t pyb_sleep_suspend (mp_obj_t self_in) {
498592 pybsleep_suspend_enter ();
499593 nlr_pop ();
500594 }
595+
501596 // an exception is always raised when exiting suspend mode
502597 enable_irq (primsk );
503598
@@ -509,6 +604,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_suspend_obj, pyb_sleep_suspend);
509604/// Enters hibernate mode. Wake up sources should have been enable prior to
510605/// calling this method.
511606STATIC mp_obj_t pyb_sleep_hibernate (mp_obj_t self_in ) {
607+ // check if we should enable timer wake-up
608+ if (pybsleep_wake_cb .timer_wake_pwrmode & PYB_PWR_MODE_HIBERNATE ) {
609+ if (!setup_timer_hibernate_wake ()) {
610+ // hibernating is not possible, wait for the forced interrupt and return
611+ pybsleep_wake_cb .timer_wake_pwrmode &= ~PYB_PWR_MODE_HIBERNATE ;
612+ HAL_Delay (FAILED_SLEEP_DELAY_MS );
613+ return mp_const_none ;
614+ }
615+ }
512616 wlan_stop ();
513617 pybsleep_flash_powerdown ();
514618 MAP_PRCMHibernateEnter ();
0 commit comments