cc3200: Add RTC callback with wake-up from sleep capability.

This commit is contained in:
danicampora 2015-03-17 13:20:15 +01:00
parent 6de1b39368
commit 181fe5016c
3 changed files with 149 additions and 23 deletions

View File

@ -54,12 +54,16 @@
/// print(rtc.datetime())
/******************************************************************************
DECLARE TYPES
DECLARE CONSTANTS
******************************************************************************/
#define PYBRTC_CLOCK_FREQUENCY_HZ 32768
#define PYBRTC_MIN_INTERVAL_VALUE 25
/******************************************************************************
DEFINE TYPES
******************************************************************************/
typedef struct {
uint32_t alarm_sec;
uint16_t alarm_msec;
uint8_t pwrmode;
byte prwmode;
} pybrtc_data_t;
/******************************************************************************
@ -92,11 +96,26 @@ void pybrtc_init(void) {
DECLARE PRIVATE FUNCTIONS
******************************************************************************/
STATIC void pyb_rtc_callback_enable (mp_obj_t self_in) {
// check the wake from param
if (pybrtc_data.prwmode & PYB_PWR_MODE_ACTIVE) {
// enable the slow clock interrupt
MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR);
}
else {
// just in case it was already enabled before
MAP_PRCMIntDisable(PRCM_INT_SLOW_CLK_CTR);
}
pybsleep_configure_timer_wakeup (pybrtc_data.prwmode);
}
STATIC void pyb_rtc_callback_disable (mp_obj_t self_in) {
// check the wake from param
if (pybrtc_data.prwmode & PYB_PWR_MODE_ACTIVE) {
// enable the slow clock interrupt
MAP_PRCMIntDisable(PRCM_INT_SLOW_CLK_CTR);
}
// disable wake from ldps and hibernate
pybsleep_configure_timer_wakeup (PYB_PWR_MODE_ACTIVE);
}
/******************************************************************************/
@ -171,6 +190,7 @@ STATIC mp_obj_t pyb_rtc_callback (mp_uint_t n_args, const mp_obj_t *pos_args, mp
// check if any parameters were passed
mp_obj_t _callback = mpcallback_find((mp_obj_t)&pyb_rtc_obj);
if (kw_args->used > 0 || _callback == mp_const_none) {
uint32_t f_mseconds = args[3].u_int;
uint32_t seconds;
uint16_t mseconds;
// get the seconds and the milliseconds from the RTC
@ -178,25 +198,26 @@ STATIC mp_obj_t pyb_rtc_callback (mp_uint_t n_args, const mp_obj_t *pos_args, mp
mseconds = RTC_CYCLES_U16MS(mseconds);
// configure the rtc alarm accordingly
seconds += args[3].u_int / 1000;
mseconds += args[3].u_int - ((args[3].u_int / 1000) * 1000);
if (mseconds > 1000) {
seconds++;
mseconds -= 1000;
}
seconds += f_mseconds / 1000;
mseconds += f_mseconds - ((f_mseconds / 1000) * 1000);
// check the wake from param
if (args[4].u_int & PYB_PWR_MODE_ACTIVE) {
MAP_PRCMRTCMatchSet(seconds, mseconds);
}
// set the match value
MAP_PRCMRTCMatchSet(seconds, mseconds);
// save the alarm config for later
pybrtc_data.alarm_sec = seconds;
pybrtc_data.alarm_msec = mseconds;
pybrtc_data.pwrmode = args[4].u_int;
// save the match data for later
pybrtc_data.prwmode = args[4].u_int;
// create the new callback
// create the callback
_callback = mpcallback_new ((mp_obj_t)&pyb_rtc_obj, args[1].u_obj, &pybrtc_cb_methods);
// set the lpds callback
pybsleep_set_timer_lpds_callback(_callback);
// the interrupt priority is ignored since is already set to to highest level by the sleep module
// to make sure that the wakeup callbacks are always called first when resuming from sleep
// enable the interrupt (the object is not relevant here, the function already knows it)
pyb_rtc_callback_enable(NULL);
}
return _callback;
}

View File

@ -67,6 +67,9 @@
#define WAKEUP_TIME_LPDS (LPDS_UP_TIME + LPDS_DOWN_TIME + USER_OFFSET) // 20 msec
#define WAKEUP_TIME_HIB (32768) // 1 s
#define FORCED_TIMER_INTERRUPT_MS (1)
#define FAILED_SLEEP_DELAY_MS (FORCED_TIMER_INTERRUPT_MS * 3)
/******************************************************************************
DECLARE PRIVATE TYPES
******************************************************************************/
@ -110,6 +113,7 @@ typedef struct {
mp_obj_t wlan_lpds_wake_cb;
mp_obj_t timer_lpds_wake_cb;
mp_obj_t gpio_lpds_wake_cb;
uint timer_wake_pwrmode;
} pybsleep_wake_cb_t;
/******************************************************************************
@ -131,6 +135,8 @@ void pybsleep_suspend_exit (void);
STATIC void pybsleep_obj_wakeup (void);
STATIC void PRCMInterruptHandler (void);
STATIC void pybsleep_iopark (void);
STATIC bool setup_timer_lpds_wake (void);
STATIC bool setup_timer_hibernate_wake (void);
/******************************************************************************
DEFINE PUBLIC FUNCTIONS
@ -151,8 +157,8 @@ void pybsleep_init0 (void) {
// disable all LPDS and hibernate wake up sources (WLAN is disabed/enabled before entering LDPS mode)
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_GPIO);
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
MAP_PRCMHibernateWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 |
PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26);
MAP_PRCMHibernateWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 |
PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26);
// store the reset casue (if it's soft reset, leave it as it is)
if (pybsleep_reset_cause != PYB_SLP_SOFT_RESET) {
@ -216,6 +222,10 @@ void pybsleep_set_timer_lpds_callback (mp_obj_t cb_obj) {
pybsleep_wake_cb.timer_lpds_wake_cb = cb_obj;
}
void pybsleep_configure_timer_wakeup (uint pwrmode) {
pybsleep_wake_cb.timer_wake_pwrmode = pwrmode;
}
/******************************************************************************
DEFINE PRIVATE FUNCTIONS
******************************************************************************/
@ -405,6 +415,9 @@ STATIC void PRCMInterruptHandler (void) {
}
break;
case PRCM_LPDS_TIMER:
// disable timer was wake-up source
pybsleep_wake_cb.timer_wake_pwrmode &= ~PYB_PWR_MODE_LPDS;
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
if (pybsleep_wake_cb.timer_lpds_wake_cb) {
mpcallback_handler(pybsleep_wake_cb.timer_lpds_wake_cb);
}
@ -466,6 +479,77 @@ STATIC void pybsleep_iopark (void) {
HWREG(0x4402E10C) = 0x00000E61;
}
STATIC bool setup_timer_lpds_wake (void) {
uint64_t t_match, t_curr, t_remaining;
// get the time remaining for the RTC timer to expire
t_match = MAP_PRCMSlowClkCtrMatchGet();
t_curr = MAP_PRCMSlowClkCtrGet();
if (t_match > t_curr) {
// get the time remaining in terms of slow clocks
t_remaining = (t_match - t_curr);
if (t_remaining > WAKEUP_TIME_LPDS) {
// subtract the time it takes for wakeup from lpds
t_remaining -= WAKEUP_TIME_LPDS;
t_remaining = (t_remaining > 0xFFFFFFFF) ? 0xFFFFFFFF: t_remaining;
// setup the LPDS wake time
MAP_PRCMLPDSIntervalSet((uint32_t)t_remaining);
// enable the wake source
MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_TIMER);
return true;
}
}
else {
// setup a timer interrupt immediately
MAP_PRCMRTCMatchSet(0, FORCED_TIMER_INTERRUPT_MS);
}
// disable the timer as wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
// LPDS wake by timer was not possible, force
// an interrupt in active mode instead
MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR);
return false;
}
STATIC bool setup_timer_hibernate_wake (void) {
uint64_t t_match, t_curr, t_remaining;
// get the time remaining for the RTC timer to expire
t_match = MAP_PRCMSlowClkCtrMatchGet();
t_curr = MAP_PRCMSlowClkCtrGet();
if (t_match > t_curr) {
// get the time remaining in terms of slow clocks
t_remaining = (t_match - t_curr);
if (t_remaining > WAKEUP_TIME_HIB) {
// subtract the time it takes for wakeup from hibernate
t_remaining -= WAKEUP_TIME_HIB;
// setup the LPDS wake time
MAP_PRCMHibernateIntervalSet((uint32_t)t_remaining);
// enable the wake source
MAP_PRCMHibernateWakeupSourceEnable(PRCM_HIB_SLOW_CLK_CTR);
return true;
}
}
else {
// setup a timer interrupt immediately
MAP_PRCMRTCMatchSet(0, FORCED_TIMER_INTERRUPT_MS);
}
// disable the timer as wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR);
// hibernate wake by timer was not possible, force
// an interrupt in active mode instead
MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR);
return false;
}
/******************************************************************************/
// Micro Python bindings; Sleep class
@ -483,6 +567,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_idle_obj, pyb_sleep_idle);
STATIC mp_obj_t pyb_sleep_suspend (mp_obj_t self_in) {
nlr_buf_t nlr;
// check if we should enable timer wake-up
if (pybsleep_wake_cb.timer_wake_pwrmode & PYB_PWR_MODE_LPDS) {
if (!setup_timer_lpds_wake()) {
// lpds entering is not possible, wait for the forced interrupt and return
pybsleep_wake_cb.timer_wake_pwrmode &= ~PYB_PWR_MODE_LPDS;
HAL_Delay (FAILED_SLEEP_DELAY_MS);
return mp_const_none;
}
}
// check if we need to enable network wake-up
if (pybsleep_wake_cb.wlan_lpds_wake_cb) {
MAP_PRCMLPDSWakeupSourceEnable (PRCM_LPDS_HOST_IRQ);
@ -498,6 +592,7 @@ STATIC mp_obj_t pyb_sleep_suspend (mp_obj_t self_in) {
pybsleep_suspend_enter();
nlr_pop();
}
// an exception is always raised when exiting suspend mode
enable_irq(primsk);
@ -509,6 +604,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_suspend_obj, pyb_sleep_suspend);
/// Enters hibernate mode. Wake up sources should have been enable prior to
/// calling this method.
STATIC mp_obj_t pyb_sleep_hibernate (mp_obj_t self_in) {
// check if we should enable timer wake-up
if (pybsleep_wake_cb.timer_wake_pwrmode & PYB_PWR_MODE_HIBERNATE) {
if (!setup_timer_hibernate_wake()) {
// hibernating is not possible, wait for the forced interrupt and return
pybsleep_wake_cb.timer_wake_pwrmode &= ~PYB_PWR_MODE_HIBERNATE;
HAL_Delay (FAILED_SLEEP_DELAY_MS);
return mp_const_none;
}
}
wlan_stop();
pybsleep_flash_powerdown();
MAP_PRCMHibernateEnter();

View File

@ -71,5 +71,6 @@ void pybsleep_remove (const mp_obj_t obj);
void pybsleep_set_wlan_lpds_callback (mp_obj_t cb_obj);
void pybsleep_set_gpio_lpds_callback (mp_obj_t cb_obj);
void pybsleep_set_timer_lpds_callback (mp_obj_t cb_obj);
void pybsleep_configure_timer_wakeup (uint pwrmode);
#endif /* PYBSLEEP_H_ */