Skip to content

[MAX32600MBED,MAXWSNENV] Added low-power ticker. #1093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@

#define DEVICE_ERROR_PATTERN 1

#define DEVICE_LOWPOWERTIMER 1

#define DEVICE_CAN 0
#define DEVICE_ETHERNET 0

Expand Down
124 changes: 78 additions & 46 deletions libraries/mbed/targets/hal/TARGET_Maxim/TARGET_MAX32600/rtc_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,56 +32,51 @@
*/

#include "rtc_api.h"
#include "lp_ticker_api.h"
#include "cmsis.h"
#include "rtc_regs.h"
#include "pwrseq_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 volatile uint32_t overflow_cnt = 0;
static uint32_t overflow_alarm = 0;

static uint64_t rtc_read64(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++;

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)
{
if(rtc_inited) {
if (rtc_inited) {
return;
}
rtc_inited = 1;

overflow_cnt = 0;

// Enable the clock to the synchronizer
MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED;

// Enable the clock to the RTC
MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;

// Set the divider from the 4kHz clock
MXC_RTCTMR->prescale = MXC_E_RTC_PRESCALE_DIV_2_0;
// Set the clock divider
MXC_RTCTMR->prescale = PRESCALE_VAL;

// Enable the overflow interrupt
MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW;

// 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_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn);
Expand All @@ -90,6 +85,12 @@ void rtc_init(void)
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE;
}

//******************************************************************************
void lp_ticker_init(void)
{
rtc_init();
}

//******************************************************************************
void rtc_free(void)
{
Expand Down Expand Up @@ -118,73 +119,104 @@ int rtc_isenabled(void)
//******************************************************************************
time_t rtc_read(void)
{
unsigned int shift_amt;
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;

// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
uint32_t ovf1, ovf2;

// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
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;
uint64_t currentUs;

// Account for a change in the default prescaler
shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
uint32_t ovf1, ovf2;
uint64_t current_us;

// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = MXC_RTCTMR->timer;
ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW;
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++;
}

current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT));

return currentUs;
return current_us;
}

//******************************************************************************
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->timer = t << shift_amt;
overflow_cnt = t >> (32 - shift_amt);
MXC_RTCTMR->timer = t << SHIFT_AMT;
overflow_cnt = t >> (32 - SHIFT_AMT);
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
unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale;
// Note: interrupts are disabled before this function is called.

// Disable the alarm while it is prepared
MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt

overflow_alarm = (wakeupUs >> (32 - shift_amt)) / 1000000;

if (overflow_alarm == overflow_cnt) {
MXC_RTCTMR->comp[0] = (wakeupUs << shift_amt) / 1000000;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
uint64_t curr_ts64 = rtc_read64();
uint64_t ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL);
if (ts64 < curr_ts64) {
if (ts64 < (curr_ts64 - 1000)) {
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
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();
}
52 changes: 3 additions & 49 deletions libraries/mbed/targets/hal/TARGET_Maxim/TARGET_MAX32600/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,14 @@
* ownership rights.
*******************************************************************************
*/

#include "sleep_api.h"
#include "us_ticker_api.h"
#include "cmsis.h"
#include "pwrman_regs.h"
#include "pwrseq_regs.h"
#include "ioman_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;

// Normal wait mode
Expand Down Expand Up @@ -80,38 +72,11 @@ static void clearAllGPIOWUD(void)
// Low-power stop mode
void deepsleep(void)
{
uint64_t sleepStartRtcUs;
uint32_t sleepStartTickerUs;
int32_t sleepDurationUs;
uint64_t sleepEndRtcUs;
uint64_t elapsedUs;

__disable_irq();

// Wait for all STDIO characters to be sent. The UART clock will stop.
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
uint32_t reg0 = MXC_PWRSEQ->reg0;
reg0 &= ~MXC_F_PWRSEQ_REG0_PWR_SVM3EN_SLP; // disable VDD3 SVM during sleep mode
Expand Down Expand Up @@ -151,19 +116,8 @@ void deepsleep(void)
// Woke up from LP1

// The RTC timer does not update until the next tick
uint64_t tempUs = rtc_read_us();
do {
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);
uint32_t temp = MXC_RTCTMR->timer;
while (MXC_RTCTMR->timer == temp);

__enable_irq();
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* ownership rights.
*******************************************************************************
*/

#include "mbed_error.h"
#include "us_ticker_api.h"
#include "PeripheralNames.h"
Expand All @@ -53,7 +53,7 @@ static inline void inc_current_cnt(uint32_t inc) {

// Overflow the ticker when the us ticker overflows
current_cnt += inc;
if(current_cnt > MAX_TICK_VAL) {
if (current_cnt > MAX_TICK_VAL) {
current_cnt -= (MAX_TICK_VAL + 1);
}
}
Expand All @@ -64,7 +64,7 @@ static inline int event_passed(uint64_t current, uint64_t event) {
// Determine if the event has already happened.
// If the event is behind the current ticker, within a window,
// 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 < current) && (event > (current - tick_win)))) {
return 1;
Expand Down Expand Up @@ -169,7 +169,7 @@ uint32_t us_ticker_read(void)
{
uint64_t current_cnt1, current_cnt2;
uint32_t term_cnt, tmr_cnt;
int intfl1, intfl2;
uint32_t intfl1, intfl2;

if (!us_ticker_inited)
us_ticker_init();
Expand All @@ -184,6 +184,7 @@ uint32_t us_ticker_read(void)
current_cnt2 = current_cnt;
} while ((current_cnt1 != current_cnt2) || (intfl1 != intfl2));

// Account for an unserviced interrupt
if (intfl1) {
current_cnt1 += term_cnt;
}
Expand All @@ -197,6 +198,7 @@ uint32_t us_ticker_read(void)
void us_ticker_set_interrupt(timestamp_t timestamp)
{
// Note: interrupts are disabled before this function is called.

US_TIMER->ctrl &= ~MXC_F_TMR_CTRL_ENABLE0; // disable timer

if (US_TIMER->intfl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@

#define DEVICE_ERROR_PATTERN 1

#define DEVICE_LOWPOWERTIMER 1

#define DEVICE_CAN 0
#define DEVICE_ETHERNET 0

Expand Down
Loading