Merge pull request #1093 from jeremybrodt/lpticker

MAX32600MBED,MAXWSNENV  - Add low-power ticker.
pull/1089/merge
Martin Kojtal 2015-05-07 08:53:25 +01:00
commit a1e04f782b
8 changed files with 178 additions and 198 deletions

View File

@ -64,6 +64,8 @@
#define DEVICE_ERROR_PATTERN 1 #define DEVICE_ERROR_PATTERN 1
#define DEVICE_LOWPOWERTIMER 1
#define DEVICE_CAN 0 #define DEVICE_CAN 0
#define DEVICE_ETHERNET 0 #define DEVICE_ETHERNET 0

View File

@ -32,56 +32,51 @@
*/ */
#include "rtc_api.h" #include "rtc_api.h"
#include "lp_ticker_api.h"
#include "cmsis.h" #include "cmsis.h"
#include "rtc_regs.h" #include "rtc_regs.h"
#include "pwrseq_regs.h" #include "pwrseq_regs.h"
#include "clkman_regs.h" #include "clkman_regs.h"
#define PRESCALE_VAL MXC_E_RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock
#define SHIFT_AMT (MXC_E_RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL)
static int rtc_inited = 0; static int rtc_inited = 0;
static volatile uint32_t overflow_cnt = 0; static volatile uint32_t overflow_cnt = 0;
static uint32_t overflow_alarm = 0;
static uint64_t rtc_read64(void);
//****************************************************************************** //******************************************************************************
static void overflow_handler(void) static void overflow_handler(void)
{ {
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS; MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_OVERFLOW;
overflow_cnt++; overflow_cnt++;
if (overflow_cnt == overflow_alarm) {
// Enable the comparator interrupt for the alarm
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
}
}
//******************************************************************************
static void alarm_handler(void)
{
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
} }
//****************************************************************************** //******************************************************************************
void rtc_init(void) void rtc_init(void)
{ {
if(rtc_inited) { if (rtc_inited) {
return; return;
} }
rtc_inited = 1; rtc_inited = 1;
overflow_cnt = 0;
// Enable the clock to the synchronizer // Enable the clock to the synchronizer
MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED; MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED;
// Enable the clock to the RTC // Enable the clock to the RTC
MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN; MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;
// Set the divider from the 4kHz clock // Set the clock divider
MXC_RTCTMR->prescale = MXC_E_RTC_PRESCALE_DIV_2_0; MXC_RTCTMR->prescale = PRESCALE_VAL;
// Enable the overflow interrupt // Enable the overflow interrupt
MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW; MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW;
// Prepare interrupt handlers // Prepare interrupt handlers
NVIC_SetVector(RTC0_IRQn, (uint32_t)alarm_handler); NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn); NVIC_EnableIRQ(RTC0_IRQn);
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler); NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn); NVIC_EnableIRQ(RTC3_IRQn);
@ -90,6 +85,12 @@ void rtc_init(void)
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE;
} }
//******************************************************************************
void lp_ticker_init(void)
{
rtc_init();
}
//****************************************************************************** //******************************************************************************
void rtc_free(void) void rtc_free(void)
{ {
@ -118,73 +119,104 @@ int rtc_isenabled(void)
//****************************************************************************** //******************************************************************************
time_t rtc_read(void) time_t rtc_read(void)
{ {
unsigned int shift_amt;
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt; uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Ensure coherency between overflow_cnt and timer // Ensure coherency between overflow_cnt and timer
do { do {
ovf_cnt_1 = overflow_cnt; ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer; timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt; ovf_cnt_2 = overflow_cnt;
} while (ovf_cnt_1 != ovf_cnt_2); } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
return (timer_cnt >> shift_amt) + (ovf_cnt_1 << (32 - shift_amt)); // Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return (timer_cnt >> SHIFT_AMT) + (ovf_cnt_1 << (32 - SHIFT_AMT));
} }
//****************************************************************************** //******************************************************************************
uint64_t rtc_read_us(void) static uint64_t rtc_read64(void)
{ {
unsigned int shift_amt;
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt; uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint64_t currentUs; uint32_t ovf1, ovf2;
uint64_t current_us;
// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Ensure coherency between overflow_cnt and timer // Ensure coherency between overflow_cnt and timer
do { do {
ovf_cnt_1 = overflow_cnt; ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer; timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt; ovf_cnt_2 = overflow_cnt;
} while (ovf_cnt_1 != ovf_cnt_2); } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
currentUs = (((uint64_t)timer_cnt * 1000000) >> shift_amt) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - shift_amt)); // Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return currentUs; current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT));
return current_us;
} }
//****************************************************************************** //******************************************************************************
void rtc_write(time_t t) void rtc_write(time_t t)
{ {
// Account for a change in the default prescaler
unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
MXC_RTCTMR->ctrl &= ~MXC_F_RTC_CTRL_ENABLE; // disable the timer while updating MXC_RTCTMR->ctrl &= ~MXC_F_RTC_CTRL_ENABLE; // disable the timer while updating
MXC_RTCTMR->timer = t << shift_amt; MXC_RTCTMR->timer = t << SHIFT_AMT;
overflow_cnt = t >> (32 - shift_amt); overflow_cnt = t >> (32 - SHIFT_AMT);
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; // enable the timer while updating MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; // enable the timer while updating
} }
//****************************************************************************** //******************************************************************************
void rtc_set_wakeup(uint64_t wakeupUs) void lp_ticker_set_interrupt(timestamp_t timestamp)
{ {
// Account for a change in the default prescaler // Note: interrupts are disabled before this function is called.
unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Disable the alarm while it is prepared // Disable the alarm while it is prepared
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0; MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt
overflow_alarm = (wakeupUs >> (32 - shift_amt)) / 1000000; uint64_t curr_ts64 = rtc_read64();
uint64_t ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL);
if (overflow_alarm == overflow_cnt) { if (ts64 < curr_ts64) {
MXC_RTCTMR->comp[0] = (wakeupUs << shift_amt) / 1000000; if (ts64 < (curr_ts64 - 1000)) {
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0; ts64 += 0x100000000ULL;
} else {
// This event has already occurred. Set the alarm to expire immediately.
MXC_RTCTMR->comp[0] = MXC_RTCTMR->timer + 2;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
return;
}
} }
MXC_RTCTMR->comp[0] = (ts64 << SHIFT_AMT) / 1000000;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
// Enable wakeup from RTC // Enable wakeup from RTC
MXC_PWRSEQ->msk_flags &= ~(MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER | MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0); MXC_PWRSEQ->msk_flags &= ~(MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER | MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0);
} }
//******************************************************************************
inline void lp_ticker_disable_interrupt(void)
{
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
}
//******************************************************************************
inline void lp_ticker_clear_interrupt(void)
{
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
}
//******************************************************************************
inline uint32_t lp_ticker_read(void)
{
return rtc_read64();
}

View File

@ -30,22 +30,14 @@
* ownership rights. * ownership rights.
******************************************************************************* *******************************************************************************
*/ */
#include "sleep_api.h" #include "sleep_api.h"
#include "us_ticker_api.h"
#include "cmsis.h" #include "cmsis.h"
#include "pwrman_regs.h" #include "pwrman_regs.h"
#include "pwrseq_regs.h" #include "pwrseq_regs.h"
#include "ioman_regs.h" #include "ioman_regs.h"
#include "rtc_regs.h" #include "rtc_regs.h"
#define MIN_DEEP_SLEEP_US 500
uint64_t rtc_read_us(void);
void rtc_set_wakeup(uint64_t wakeupUs);
void us_ticker_deinit(void);
void us_ticker_set(timestamp_t timestamp);
static mxc_uart_regs_t *stdio_uart = (mxc_uart_regs_t*)STDIO_UART; static mxc_uart_regs_t *stdio_uart = (mxc_uart_regs_t*)STDIO_UART;
// Normal wait mode // Normal wait mode
@ -80,38 +72,11 @@ static void clearAllGPIOWUD(void)
// Low-power stop mode // Low-power stop mode
void deepsleep(void) void deepsleep(void)
{ {
uint64_t sleepStartRtcUs;
uint32_t sleepStartTickerUs;
int32_t sleepDurationUs;
uint64_t sleepEndRtcUs;
uint64_t elapsedUs;
__disable_irq(); __disable_irq();
// Wait for all STDIO characters to be sent. The UART clock will stop. // Wait for all STDIO characters to be sent. The UART clock will stop.
while (stdio_uart->status & MXC_F_UART_STATUS_TX_BUSY); while (stdio_uart->status & MXC_F_UART_STATUS_TX_BUSY);
// Record the current times
sleepStartRtcUs = rtc_read_us();
sleepStartTickerUs = us_ticker_read();
// Get the next mbed timer expiration
timestamp_t next_event = 0;
us_ticker_get_next_timestamp(&next_event);
sleepDurationUs = next_event - sleepStartTickerUs;
if (sleepDurationUs < MIN_DEEP_SLEEP_US) {
/* The next wakeup is too soon. */
__enable_irq();
return;
}
// Disable the us_ticker. It won't be clocked in DeepSleep
us_ticker_deinit();
// Prepare to wakeup from the RTC
rtc_set_wakeup(sleepStartRtcUs + sleepDurationUs);
// Prepare for LP1 // Prepare for LP1
uint32_t reg0 = MXC_PWRSEQ->reg0; uint32_t reg0 = MXC_PWRSEQ->reg0;
reg0 &= ~MXC_F_PWRSEQ_REG0_PWR_SVM3EN_SLP; // disable VDD3 SVM during sleep mode reg0 &= ~MXC_F_PWRSEQ_REG0_PWR_SVM3EN_SLP; // disable VDD3 SVM during sleep mode
@ -151,19 +116,8 @@ void deepsleep(void)
// Woke up from LP1 // Woke up from LP1
// The RTC timer does not update until the next tick // The RTC timer does not update until the next tick
uint64_t tempUs = rtc_read_us(); uint32_t temp = MXC_RTCTMR->timer;
do { while (MXC_RTCTMR->timer == temp);
sleepEndRtcUs = rtc_read_us();
} while(sleepEndRtcUs == tempUs);
// Get the elapsed time from the RTC. Wakeup could have been from some other event.
elapsedUs = sleepEndRtcUs - sleepStartRtcUs;
// Update the us_ticker. It was not clocked during DeepSleep
us_ticker_init();
us_ticker_set(sleepStartTickerUs + elapsedUs);
us_ticker_get_next_timestamp(&next_event);
us_ticker_set_interrupt(next_event);
__enable_irq(); __enable_irq();
} }

View File

@ -30,7 +30,7 @@
* ownership rights. * ownership rights.
******************************************************************************* *******************************************************************************
*/ */
#include "mbed_error.h" #include "mbed_error.h"
#include "us_ticker_api.h" #include "us_ticker_api.h"
#include "PeripheralNames.h" #include "PeripheralNames.h"
@ -53,7 +53,7 @@ static inline void inc_current_cnt(uint32_t inc) {
// Overflow the ticker when the us ticker overflows // Overflow the ticker when the us ticker overflows
current_cnt += inc; current_cnt += inc;
if(current_cnt > MAX_TICK_VAL) { if (current_cnt > MAX_TICK_VAL) {
current_cnt -= (MAX_TICK_VAL + 1); current_cnt -= (MAX_TICK_VAL + 1);
} }
} }
@ -64,7 +64,7 @@ static inline int event_passed(uint64_t current, uint64_t event) {
// Determine if the event has already happened. // Determine if the event has already happened.
// If the event is behind the current ticker, within a window, // If the event is behind the current ticker, within a window,
// then the event has already happened. // then the event has already happened.
if(((current < tick_win) && ((event < current) || if (((current < tick_win) && ((event < current) ||
(event > (MAX_TICK_VAL - (tick_win - current))))) || (event > (MAX_TICK_VAL - (tick_win - current))))) ||
((event < current) && (event > (current - tick_win)))) { ((event < current) && (event > (current - tick_win)))) {
return 1; return 1;
@ -169,7 +169,7 @@ uint32_t us_ticker_read(void)
{ {
uint64_t current_cnt1, current_cnt2; uint64_t current_cnt1, current_cnt2;
uint32_t term_cnt, tmr_cnt; uint32_t term_cnt, tmr_cnt;
int intfl1, intfl2; uint32_t intfl1, intfl2;
if (!us_ticker_inited) if (!us_ticker_inited)
us_ticker_init(); us_ticker_init();
@ -184,6 +184,7 @@ uint32_t us_ticker_read(void)
current_cnt2 = current_cnt; current_cnt2 = current_cnt;
} while ((current_cnt1 != current_cnt2) || (intfl1 != intfl2)); } while ((current_cnt1 != current_cnt2) || (intfl1 != intfl2));
// Account for an unserviced interrupt
if (intfl1) { if (intfl1) {
current_cnt1 += term_cnt; current_cnt1 += term_cnt;
} }
@ -197,6 +198,7 @@ uint32_t us_ticker_read(void)
void us_ticker_set_interrupt(timestamp_t timestamp) void us_ticker_set_interrupt(timestamp_t timestamp)
{ {
// Note: interrupts are disabled before this function is called. // Note: interrupts are disabled before this function is called.
US_TIMER->ctrl &= ~MXC_F_TMR_CTRL_ENABLE0; // disable timer US_TIMER->ctrl &= ~MXC_F_TMR_CTRL_ENABLE0; // disable timer
if (US_TIMER->intfl) { if (US_TIMER->intfl) {

View File

@ -64,6 +64,8 @@
#define DEVICE_ERROR_PATTERN 1 #define DEVICE_ERROR_PATTERN 1
#define DEVICE_LOWPOWERTIMER 1
#define DEVICE_CAN 0 #define DEVICE_CAN 0
#define DEVICE_ETHERNET 0 #define DEVICE_ETHERNET 0

View File

@ -32,56 +32,51 @@
*/ */
#include "rtc_api.h" #include "rtc_api.h"
#include "lp_ticker_api.h"
#include "cmsis.h" #include "cmsis.h"
#include "rtc_regs.h" #include "rtc_regs.h"
#include "pwrseq_regs.h" #include "pwrseq_regs.h"
#include "clkman_regs.h" #include "clkman_regs.h"
#define PRESCALE_VAL MXC_E_RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock
#define SHIFT_AMT (MXC_E_RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL)
static int rtc_inited = 0; static int rtc_inited = 0;
static volatile uint32_t overflow_cnt = 0; static volatile uint32_t overflow_cnt = 0;
static uint32_t overflow_alarm = 0;
static uint64_t rtc_read64(void);
//****************************************************************************** //******************************************************************************
static void overflow_handler(void) static void overflow_handler(void)
{ {
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS; MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_OVERFLOW;
overflow_cnt++; overflow_cnt++;
if (overflow_cnt == overflow_alarm) {
// Enable the comparator interrupt for the alarm
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
}
}
//******************************************************************************
static void alarm_handler(void)
{
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
} }
//****************************************************************************** //******************************************************************************
void rtc_init(void) void rtc_init(void)
{ {
if(rtc_inited) { if (rtc_inited) {
return; return;
} }
rtc_inited = 1; rtc_inited = 1;
overflow_cnt = 0;
// Enable the clock to the synchronizer // Enable the clock to the synchronizer
MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED; MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED;
// Enable the clock to the RTC // Enable the clock to the RTC
MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN; MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;
// Set the divider from the 4kHz clock // Set the clock divider
MXC_RTCTMR->prescale = MXC_E_RTC_PRESCALE_DIV_2_0; MXC_RTCTMR->prescale = PRESCALE_VAL;
// Enable the overflow interrupt // Enable the overflow interrupt
MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW; MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW;
// Prepare interrupt handlers // Prepare interrupt handlers
NVIC_SetVector(RTC0_IRQn, (uint32_t)alarm_handler); NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn); NVIC_EnableIRQ(RTC0_IRQn);
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler); NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn); NVIC_EnableIRQ(RTC3_IRQn);
@ -90,6 +85,12 @@ void rtc_init(void)
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE;
} }
//******************************************************************************
void lp_ticker_init(void)
{
rtc_init();
}
//****************************************************************************** //******************************************************************************
void rtc_free(void) void rtc_free(void)
{ {
@ -118,73 +119,104 @@ int rtc_isenabled(void)
//****************************************************************************** //******************************************************************************
time_t rtc_read(void) time_t rtc_read(void)
{ {
unsigned int shift_amt;
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt; uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Ensure coherency between overflow_cnt and timer // Ensure coherency between overflow_cnt and timer
do { do {
ovf_cnt_1 = overflow_cnt; ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer; timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt; ovf_cnt_2 = overflow_cnt;
} while (ovf_cnt_1 != ovf_cnt_2); } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
return (timer_cnt >> shift_amt) + (ovf_cnt_1 << (32 - shift_amt)); // Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return (timer_cnt >> SHIFT_AMT) + (ovf_cnt_1 << (32 - SHIFT_AMT));
} }
//****************************************************************************** //******************************************************************************
uint64_t rtc_read_us(void) static uint64_t rtc_read64(void)
{ {
unsigned int shift_amt;
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt; uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint64_t currentUs; uint32_t ovf1, ovf2;
uint64_t current_us;
// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Ensure coherency between overflow_cnt and timer // Ensure coherency between overflow_cnt and timer
do { do {
ovf_cnt_1 = overflow_cnt; ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer; timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt; ovf_cnt_2 = overflow_cnt;
} while (ovf_cnt_1 != ovf_cnt_2); } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
currentUs = (((uint64_t)timer_cnt * 1000000) >> shift_amt) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - shift_amt)); // Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return currentUs; current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT));
return current_us;
} }
//****************************************************************************** //******************************************************************************
void rtc_write(time_t t) void rtc_write(time_t t)
{ {
// Account for a change in the default prescaler
unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
MXC_RTCTMR->ctrl &= ~MXC_F_RTC_CTRL_ENABLE; // disable the timer while updating MXC_RTCTMR->ctrl &= ~MXC_F_RTC_CTRL_ENABLE; // disable the timer while updating
MXC_RTCTMR->timer = t << shift_amt; MXC_RTCTMR->timer = t << SHIFT_AMT;
overflow_cnt = t >> (32 - shift_amt); overflow_cnt = t >> (32 - SHIFT_AMT);
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; // enable the timer while updating MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; // enable the timer while updating
} }
//****************************************************************************** //******************************************************************************
void rtc_set_wakeup(uint64_t wakeupUs) void lp_ticker_set_interrupt(timestamp_t timestamp)
{ {
// Account for a change in the default prescaler // Note: interrupts are disabled before this function is called.
unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Disable the alarm while it is prepared // Disable the alarm while it is prepared
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0; MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt
overflow_alarm = (wakeupUs >> (32 - shift_amt)) / 1000000; uint64_t curr_ts64 = rtc_read64();
uint64_t ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL);
if (overflow_alarm == overflow_cnt) { if (ts64 < curr_ts64) {
MXC_RTCTMR->comp[0] = (wakeupUs << shift_amt) / 1000000; if (ts64 < (curr_ts64 - 1000)) {
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0; ts64 += 0x100000000ULL;
} else {
// This event has already occurred. Set the alarm to expire immediately.
MXC_RTCTMR->comp[0] = MXC_RTCTMR->timer + 2;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
return;
}
} }
MXC_RTCTMR->comp[0] = (ts64 << SHIFT_AMT) / 1000000;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
// Enable wakeup from RTC // Enable wakeup from RTC
MXC_PWRSEQ->msk_flags &= ~(MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER | MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0); MXC_PWRSEQ->msk_flags &= ~(MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER | MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0);
} }
//******************************************************************************
inline void lp_ticker_disable_interrupt(void)
{
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
}
//******************************************************************************
inline void lp_ticker_clear_interrupt(void)
{
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
}
//******************************************************************************
inline uint32_t lp_ticker_read(void)
{
return rtc_read64();
}

View File

@ -30,22 +30,14 @@
* ownership rights. * ownership rights.
******************************************************************************* *******************************************************************************
*/ */
#include "sleep_api.h" #include "sleep_api.h"
#include "us_ticker_api.h"
#include "cmsis.h" #include "cmsis.h"
#include "pwrman_regs.h" #include "pwrman_regs.h"
#include "pwrseq_regs.h" #include "pwrseq_regs.h"
#include "ioman_regs.h" #include "ioman_regs.h"
#include "rtc_regs.h" #include "rtc_regs.h"
#define MIN_DEEP_SLEEP_US 500
uint64_t rtc_read_us(void);
void rtc_set_wakeup(uint64_t wakeupUs);
void us_ticker_deinit(void);
void us_ticker_set(timestamp_t timestamp);
static mxc_uart_regs_t *stdio_uart = (mxc_uart_regs_t*)STDIO_UART; static mxc_uart_regs_t *stdio_uart = (mxc_uart_regs_t*)STDIO_UART;
// Normal wait mode // Normal wait mode
@ -80,38 +72,11 @@ static void clearAllGPIOWUD(void)
// Low-power stop mode // Low-power stop mode
void deepsleep(void) void deepsleep(void)
{ {
uint64_t sleepStartRtcUs;
uint32_t sleepStartTickerUs;
int32_t sleepDurationUs;
uint64_t sleepEndRtcUs;
uint64_t elapsedUs;
__disable_irq(); __disable_irq();
// Wait for all STDIO characters to be sent. The UART clock will stop. // Wait for all STDIO characters to be sent. The UART clock will stop.
while (stdio_uart->status & MXC_F_UART_STATUS_TX_BUSY); while (stdio_uart->status & MXC_F_UART_STATUS_TX_BUSY);
// Record the current times
sleepStartRtcUs = rtc_read_us();
sleepStartTickerUs = us_ticker_read();
// Get the next mbed timer expiration
timestamp_t next_event = 0;
us_ticker_get_next_timestamp(&next_event);
sleepDurationUs = next_event - sleepStartTickerUs;
if (sleepDurationUs < MIN_DEEP_SLEEP_US) {
/* The next wakeup is too soon. */
__enable_irq();
return;
}
// Disable the us_ticker. It won't be clocked in DeepSleep
us_ticker_deinit();
// Prepare to wakeup from the RTC
rtc_set_wakeup(sleepStartRtcUs + sleepDurationUs);
// Prepare for LP1 // Prepare for LP1
uint32_t reg0 = MXC_PWRSEQ->reg0; uint32_t reg0 = MXC_PWRSEQ->reg0;
reg0 &= ~MXC_F_PWRSEQ_REG0_PWR_SVM3EN_SLP; // disable VDD3 SVM during sleep mode reg0 &= ~MXC_F_PWRSEQ_REG0_PWR_SVM3EN_SLP; // disable VDD3 SVM during sleep mode
@ -151,19 +116,8 @@ void deepsleep(void)
// Woke up from LP1 // Woke up from LP1
// The RTC timer does not update until the next tick // The RTC timer does not update until the next tick
uint64_t tempUs = rtc_read_us(); uint32_t temp = MXC_RTCTMR->timer;
do { while (MXC_RTCTMR->timer == temp);
sleepEndRtcUs = rtc_read_us();
} while(sleepEndRtcUs == tempUs);
// Get the elapsed time from the RTC. Wakeup could have been from some other event.
elapsedUs = sleepEndRtcUs - sleepStartRtcUs;
// Update the us_ticker. It was not clocked during DeepSleep
us_ticker_init();
us_ticker_set(sleepStartTickerUs + elapsedUs);
us_ticker_get_next_timestamp(&next_event);
us_ticker_set_interrupt(next_event);
__enable_irq(); __enable_irq();
} }

View File

@ -30,7 +30,7 @@
* ownership rights. * ownership rights.
******************************************************************************* *******************************************************************************
*/ */
#include "mbed_error.h" #include "mbed_error.h"
#include "us_ticker_api.h" #include "us_ticker_api.h"
#include "PeripheralNames.h" #include "PeripheralNames.h"
@ -53,7 +53,7 @@ static inline void inc_current_cnt(uint32_t inc) {
// Overflow the ticker when the us ticker overflows // Overflow the ticker when the us ticker overflows
current_cnt += inc; current_cnt += inc;
if(current_cnt > MAX_TICK_VAL) { if (current_cnt > MAX_TICK_VAL) {
current_cnt -= (MAX_TICK_VAL + 1); current_cnt -= (MAX_TICK_VAL + 1);
} }
} }
@ -64,7 +64,7 @@ static inline int event_passed(uint64_t current, uint64_t event) {
// Determine if the event has already happened. // Determine if the event has already happened.
// If the event is behind the current ticker, within a window, // If the event is behind the current ticker, within a window,
// then the event has already happened. // then the event has already happened.
if(((current < tick_win) && ((event < current) || if (((current < tick_win) && ((event < current) ||
(event > (MAX_TICK_VAL - (tick_win - current))))) || (event > (MAX_TICK_VAL - (tick_win - current))))) ||
((event < current) && (event > (current - tick_win)))) { ((event < current) && (event > (current - tick_win)))) {
return 1; return 1;
@ -169,7 +169,7 @@ uint32_t us_ticker_read(void)
{ {
uint64_t current_cnt1, current_cnt2; uint64_t current_cnt1, current_cnt2;
uint32_t term_cnt, tmr_cnt; uint32_t term_cnt, tmr_cnt;
int intfl1, intfl2; uint32_t intfl1, intfl2;
if (!us_ticker_inited) if (!us_ticker_inited)
us_ticker_init(); us_ticker_init();
@ -184,6 +184,7 @@ uint32_t us_ticker_read(void)
current_cnt2 = current_cnt; current_cnt2 = current_cnt;
} while ((current_cnt1 != current_cnt2) || (intfl1 != intfl2)); } while ((current_cnt1 != current_cnt2) || (intfl1 != intfl2));
// Account for an unserviced interrupt
if (intfl1) { if (intfl1) {
current_cnt1 += term_cnt; current_cnt1 += term_cnt;
} }
@ -197,6 +198,7 @@ uint32_t us_ticker_read(void)
void us_ticker_set_interrupt(timestamp_t timestamp) void us_ticker_set_interrupt(timestamp_t timestamp)
{ {
// Note: interrupts are disabled before this function is called. // Note: interrupts are disabled before this function is called.
US_TIMER->ctrl &= ~MXC_F_TMR_CTRL_ENABLE0; // disable timer US_TIMER->ctrl &= ~MXC_F_TMR_CTRL_ENABLE0; // disable timer
if (US_TIMER->intfl) { if (US_TIMER->intfl) {