diff --git a/cpu/cc26xx-cc13xx/dev/soc-rtc.c b/cpu/cc26xx-cc13xx/dev/soc-rtc.c index 08d9a7cce..398f14d6a 100644 --- a/cpu/cc26xx-cc13xx/dev/soc-rtc.c +++ b/cpu/cc26xx-cc13xx/dev/soc-rtc.c @@ -81,11 +81,13 @@ soc_rtc_init(void) ti_lib_aon_rtc_event_clear(AON_RTC_CH0); ti_lib_aon_rtc_event_clear(AON_RTC_CH1); + ti_lib_aon_rtc_event_clear(AON_RTC_CH2); /* Setup the wakeup event */ ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC_CH0); ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC_CH1); - ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1); + ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_RTC_CH2); + ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1 | AON_RTC_CH2); HWREG(AON_RTC_BASE + AON_RTC_O_SEC) = SOC_RTC_START_TICK_COUNT; @@ -123,7 +125,7 @@ soc_rtc_get_next_trigger() void soc_rtc_schedule_one_shot(uint32_t channel, uint32_t ticks) { - if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1)) { + if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1) && (channel != AON_RTC_CH2)) { return; } @@ -170,6 +172,12 @@ soc_rtc_isr(void) rtimer_run_next(); } + if(ti_lib_aon_rtc_event_get(AON_RTC_CH2)) { + /* after sleep; since a rtimer is already scheduled, do nothing */ + ti_lib_aon_rtc_channel_disable(AON_RTC_CH2); + HWREG(AON_RTC_BASE + AON_RTC_O_EVFLAGS) = AON_RTC_EVFLAGS_CH2; + } + ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ diff --git a/cpu/cc26xx-cc13xx/lpm.c b/cpu/cc26xx-cc13xx/lpm.c index 686065c73..3253202c2 100644 --- a/cpu/cc26xx-cc13xx/lpm.c +++ b/cpu/cc26xx-cc13xx/lpm.c @@ -77,12 +77,13 @@ LIST(modules_list); * Don't consider standby mode if the next AON RTC event is scheduled to fire * in less than STANDBY_MIN_DURATION rtimer ticks */ -#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 11) -#define MINIMAL_SAFE_SCHEDUAL 8u +#define STANDBY_MIN_DURATION (RTIMER_SECOND / 100) /* 10.0 ms */ + +/* Wake up this much time earlier before the next rtimer */ +#define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */ + #define MAX_SLEEP_TIME RTIMER_SECOND -#define DEFAULT_SLEEP_TIME RTIMER_SECOND -/*---------------------------------------------------------------------------*/ -#define CLK_TO_RT(c) ((c) * (RTIMER_SECOND / CLOCK_SECOND)) +#define MINIMAL_SAFE_SCHEDULE 8u /*---------------------------------------------------------------------------*/ /* Prototype of a function in clock.c. Called every time we come out of DS */ void clock_update(void); @@ -241,14 +242,124 @@ wake_up(void) module->wakeup(); } } + +#if CC2650_FAST_RADIO_STARTUP + /* + * Trigger a switch to the XOSC, so that we can subsequently use the RF FS + */ + oscillators_request_hf_xosc(); +#endif +} +/*---------------------------------------------------------------------------*/ +static int +setup_sleep_mode(rtimer_clock_t *next_timer) +{ + lpm_registered_module_t *module; + uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; + + rtimer_clock_t now = RTIMER_NOW(); + const rtimer_clock_t max_sleep = now + MAX_SLEEP_TIME; + + /* next_timer will hold the time of the next system wakeup due to a timer*/ + *next_timer = max_sleep; + + /* Check if any events fired before we turned interrupts off. If so, abort */ + if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) { + return LPM_MODE_AWAKE; + } + + if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) { + rtimer_clock_t next_rtimer; + /* find out the timer of the next rtimer interrupt */ + next_rtimer = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH0); + if(RTIMER_CLOCK_LT(next_rtimer, now + 2)) { + return LPM_MODE_AWAKE; + } + if(RTIMER_CLOCK_LT(next_rtimer, now + STANDBY_MIN_DURATION)) { + return LPM_MODE_SLEEP; + } + *next_timer = next_rtimer; + } + + /* also find out the timer of the next etimer */ + if(etimer_pending()) { + int32_t until_next_etimer; + rtimer_clock_t next_etimer; + + until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time(); + if(until_next_etimer < 1) { + return LPM_MODE_AWAKE; + } + + next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND)); + if(RTIMER_CLOCK_LT(next_etimer, now + STANDBY_MIN_DURATION)) { + /* ensure that we schedule sleep a minimal number of ticks into the + future */ + soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MINIMAL_SAFE_SCHEDULE); + return LPM_MODE_SLEEP; + } + + if(RTIMER_CLOCK_LT(max_sleep, next_etimer)) { + /* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept + for too long. */ + if(RTIMER_CLOCK_LT(max_sleep, *next_timer)) { + soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep); + } + } else { + /* Reschedule AON RTC CH1 to fire just in time for the next etimer event */ + soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer); + } + + if(RTIMER_CLOCK_LT(next_etimer, *next_timer)) { + /* set `next_timer` to the time the first etimer fires */ + *next_timer = next_etimer; + } + } + + /* Collect max allowed PM permission from interested modules */ + for(module = list_head(modules_list); module != NULL; + module = module->next) { + if(module->request_max_pm) { + uint8_t module_pm = module->request_max_pm(); + if(module_pm < max_pm) { + max_pm = module_pm; + } + } + } + + return max_pm; +} +/*---------------------------------------------------------------------------*/ +void +lpm_sleep(void) +{ + ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM); + + /* We are only interested in IRQ energest while idle or in LPM */ + ENERGEST_IRQ_RESTORE(irq_energest); + + /* Just to be on the safe side, explicitly disable Deep Sleep */ + HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP); + + ti_lib_prcm_sleep(); + + /* Remember IRQ energest for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + + ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU); } /*---------------------------------------------------------------------------*/ static void -deep_sleep(void) +deep_sleep(rtimer_clock_t next_timer) { uint32_t domains = LOCKABLE_DOMAINS; lpm_registered_module_t *module; +#if CC2650_FAST_RADIO_STARTUP + /* schedule a wakeup briefly before the next etimer/rtimer to wake up the system */ + soc_rtc_schedule_one_shot(AON_RTC_CH2, next_timer - SLEEP_GUARD_TIME); +#endif + /* * Notify all registered modules that we are dropping to mode X. We do not * need to do this for simple sleep. @@ -304,7 +415,8 @@ deep_sleep(void) * turn back off. * * If the radio is on, we won't even reach here, and if it's off the HF - * clock source should already be the HF RC. + * clock source should already be the HF RC, unless CC2650_FAST_RADIO_STARTUP + * is defined. * * Nevertheless, request the switch to the HF RC explicitly here. */ @@ -370,131 +482,32 @@ deep_sleep(void) * unpending events so the handlers can fire */ wake_up(); -} -/*---------------------------------------------------------------------------*/ -static void -safe_schedule_rtimer(rtimer_clock_t time, rtimer_clock_t now, int pm) -{ - rtimer_clock_t min_sleep; - rtimer_clock_t max_sleep; - min_sleep = now + MINIMAL_SAFE_SCHEDUAL; - max_sleep = now + MAX_SLEEP_TIME; - - if(RTIMER_CLOCK_LT(time, min_sleep)) { - /* ensure that we schedule sleep a minimal number of ticks into the - future */ - soc_rtc_schedule_one_shot(AON_RTC_CH1, min_sleep); - } else if((pm == LPM_MODE_SLEEP) && RTIMER_CLOCK_LT(max_sleep, time)) { - /* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept - for too long. */ - soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep); - } else { - soc_rtc_schedule_one_shot(AON_RTC_CH1, time); - } -} -/*---------------------------------------------------------------------------*/ -static int -setup_sleep_mode(void) -{ - rtimer_clock_t et_distance = 0; - - lpm_registered_module_t *module; - int max_pm; - int module_pm; - int etimer_is_pending; - rtimer_clock_t now; - rtimer_clock_t et_time; - rtimer_clock_t next_trig; - - max_pm = LPM_MODE_MAX_SUPPORTED; - now = RTIMER_NOW(); - - if((LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE) || process_nevents()) { - return LPM_MODE_AWAKE; - } - - etimer_is_pending = etimer_pending(); - - if(etimer_is_pending) { - et_distance = CLK_TO_RT(etimer_next_expiration_time() - clock_time()); - - if(RTIMER_CLOCK_LT(et_distance, 1)) { - /* there is an etimer which is already expired; we shouldn't go to - sleep at all */ - return LPM_MODE_AWAKE; - } - } - - next_trig = soc_rtc_get_next_trigger(); - if(RTIMER_CLOCK_LT(next_trig, now + STANDBY_MIN_DURATION)) { - return LPM_MODE_SLEEP; - } - - /* Collect max allowed PM permission from interested modules */ - for(module = list_head(modules_list); module != NULL; - module = module->next) { - if(module->request_max_pm) { - module_pm = module->request_max_pm(); - if(module_pm < max_pm) { - max_pm = module_pm; - } - } - } - - /* Reschedule AON RTC CH1 to fire just in time for the next etimer event */ - if(etimer_is_pending) { - et_time = soc_rtc_last_isr_time() + et_distance; - - safe_schedule_rtimer(et_time, now, max_pm); - } else { - /* set a maximal sleep period if no etimers are queued */ - soc_rtc_schedule_one_shot(AON_RTC_CH1, now + DEFAULT_SLEEP_TIME); - } - - return max_pm; + ti_lib_int_master_enable(); } /*---------------------------------------------------------------------------*/ void lpm_drop() { uint8_t max_pm; + rtimer_clock_t next_timer; /* Critical. Don't get interrupted! */ ti_lib_int_master_disable(); - max_pm = setup_sleep_mode(); + max_pm = setup_sleep_mode(&next_timer); /* Drop */ if(max_pm == LPM_MODE_SLEEP) { lpm_sleep(); } else if(max_pm == LPM_MODE_DEEP_SLEEP) { - deep_sleep(); + deep_sleep(next_timer); } ti_lib_int_master_enable(); } /*---------------------------------------------------------------------------*/ void -lpm_sleep(void) -{ - ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM); - - /* We are only interested in IRQ energest while idle or in LPM */ - ENERGEST_IRQ_RESTORE(irq_energest); - - /* Just to be on the safe side, explicitly disable Deep Sleep */ - HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP); - - ti_lib_prcm_sleep(); - - /* Remember IRQ energest for next pass */ - ENERGEST_IRQ_SAVE(irq_energest); - - ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU); -} -/*---------------------------------------------------------------------------*/ -void lpm_register_module(lpm_registered_module_t *module) { list_add(modules_list, module); @@ -512,7 +525,7 @@ lpm_init() list_init(modules_list); /* Always wake up on any DIO edge detection */ - ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_IO); + ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU3, AON_EVENT_IO); } /*---------------------------------------------------------------------------*/ void diff --git a/platform/srf06-cc26xx/contiki-main.c b/platform/srf06-cc26xx/contiki-main.c index b3d64f24a..b3f5c37f7 100644 --- a/platform/srf06-cc26xx/contiki-main.c +++ b/platform/srf06-cc26xx/contiki-main.c @@ -149,6 +149,11 @@ main(void) /* Set the LF XOSC as the LF system clock source */ oscillators_select_lf_xosc(); +#if CC2650_FAST_RADIO_STARTUP + /* Also request HF XOSC to start up */ + oscillators_request_hf_xosc(); +#endif + lpm_init(); board_init();