Merge pull request #472 from ADVANSEE/cc2538-clock-adjust-etimer-poll
cc2538: clock: Fix clock / timer issues with PM1/2
This commit is contained in:
commit
602f834caf
|
@ -38,7 +38,11 @@
|
||||||
*
|
*
|
||||||
* To implement the clock functionality, we use the SysTick peripheral on the
|
* To implement the clock functionality, we use the SysTick peripheral on the
|
||||||
* cortex-M3. We run the system clock at 16 MHz and we set the SysTick to give
|
* cortex-M3. We run the system clock at 16 MHz and we set the SysTick to give
|
||||||
* us 128 interrupts / sec
|
* us 128 interrupts / sec. However, the Sleep Timer counter value is used for
|
||||||
|
* the number of elapsed ticks in order to avoid a significant time drift caused
|
||||||
|
* by PM1/2. Contrary to the Sleep Timer, the SysTick peripheral is indeed
|
||||||
|
* frozen during PM1/2, so adjusting upon wake-up a tick counter based on this
|
||||||
|
* peripheral would hardly be accurate.
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
* \file
|
* \file
|
||||||
|
@ -52,14 +56,15 @@
|
||||||
#include "dev/sys-ctrl.h"
|
#include "dev/sys-ctrl.h"
|
||||||
|
|
||||||
#include "sys/energest.h"
|
#include "sys/energest.h"
|
||||||
|
#include "sys/etimer.h"
|
||||||
|
#include "sys/rtimer.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#define RTIMER_CLOCK_TICK_RATIO (RTIMER_SECOND / CLOCK_SECOND)
|
||||||
#define RELOAD_VALUE (125000 - 1) /** Fire 128 times / sec */
|
#define RELOAD_VALUE (125000 - 1) /** Fire 128 times / sec */
|
||||||
|
|
||||||
static volatile clock_time_t count;
|
static volatile uint64_t rt_ticks_startup = 0, rt_ticks_epoch = 0;
|
||||||
static volatile unsigned long secs = 0;
|
|
||||||
static volatile uint8_t second_countdown = CLOCK_SECOND;
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/**
|
/**
|
||||||
* \brief Arch-specific implementation of clock_init for the cc2538
|
* \brief Arch-specific implementation of clock_init for the cc2538
|
||||||
|
@ -75,8 +80,6 @@ static volatile uint8_t second_countdown = CLOCK_SECOND;
|
||||||
void
|
void
|
||||||
clock_init(void)
|
clock_init(void)
|
||||||
{
|
{
|
||||||
count = 0;
|
|
||||||
|
|
||||||
REG(SYSTICK_STRELOAD) = RELOAD_VALUE;
|
REG(SYSTICK_STRELOAD) = RELOAD_VALUE;
|
||||||
|
|
||||||
/* System clock source, Enable */
|
/* System clock source, Enable */
|
||||||
|
@ -109,19 +112,19 @@ clock_init(void)
|
||||||
CCIF clock_time_t
|
CCIF clock_time_t
|
||||||
clock_time(void)
|
clock_time(void)
|
||||||
{
|
{
|
||||||
return count;
|
return rt_ticks_startup / RTIMER_CLOCK_TICK_RATIO;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
clock_set_seconds(unsigned long sec)
|
clock_set_seconds(unsigned long sec)
|
||||||
{
|
{
|
||||||
secs = sec;
|
rt_ticks_epoch = (uint64_t)sec * RTIMER_SECOND;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
CCIF unsigned long
|
CCIF unsigned long
|
||||||
clock_seconds(void)
|
clock_seconds(void)
|
||||||
{
|
{
|
||||||
return secs;
|
return rt_ticks_epoch / RTIMER_SECOND;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
|
@ -160,63 +163,84 @@ clock_delay(unsigned int i)
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/**
|
/**
|
||||||
* \brief Adjust the clock by moving it forward by a number of ticks
|
* \brief Update the software clock ticks and seconds
|
||||||
* \param ticks The number of ticks
|
*
|
||||||
|
* This function is used to update the software tick counters whenever the
|
||||||
|
* system clock might have changed, which can occur upon a SysTick ISR or upon
|
||||||
|
* wake-up from PM1/2.
|
||||||
|
*
|
||||||
|
* For the software clock ticks counter, the Sleep Timer counter value is used
|
||||||
|
* as the base tick value, and extended to a 64-bit value thanks to a detection
|
||||||
|
* of wraparounds.
|
||||||
|
*
|
||||||
|
* For the seconds counter, the changes of the Sleep Timer counter value are
|
||||||
|
* added to the reference time, which is either the startup time or the value
|
||||||
|
* passed to clock_set_seconds().
|
||||||
|
*
|
||||||
|
* This function polls the etimer process if an etimer has expired.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
update_ticks(void)
|
||||||
|
{
|
||||||
|
rtimer_clock_t now;
|
||||||
|
uint64_t prev_rt_ticks_startup, cur_rt_ticks_startup;
|
||||||
|
uint32_t cur_rt_ticks_startup_hi;
|
||||||
|
|
||||||
|
now = RTIMER_NOW();
|
||||||
|
prev_rt_ticks_startup = rt_ticks_startup;
|
||||||
|
|
||||||
|
cur_rt_ticks_startup_hi = prev_rt_ticks_startup >> 32;
|
||||||
|
if(now < (rtimer_clock_t)prev_rt_ticks_startup) {
|
||||||
|
cur_rt_ticks_startup_hi++;
|
||||||
|
}
|
||||||
|
cur_rt_ticks_startup = (uint64_t)cur_rt_ticks_startup_hi << 32 | now;
|
||||||
|
rt_ticks_startup = cur_rt_ticks_startup;
|
||||||
|
|
||||||
|
rt_ticks_epoch += cur_rt_ticks_startup - prev_rt_ticks_startup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inform the etimer library that the system clock has changed and that an
|
||||||
|
* etimer might have expired.
|
||||||
|
*/
|
||||||
|
if(etimer_pending()) {
|
||||||
|
etimer_request_poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Adjust the clock following missed SysTick ISRs
|
||||||
*
|
*
|
||||||
* This function is useful when coming out of PM1/2, during which the system
|
* This function is useful when coming out of PM1/2, during which the system
|
||||||
* clock is stopped. We adjust the clock by moving it forward by a number of
|
* clock is stopped. We adjust the clock counters like after any SysTick ISR.
|
||||||
* ticks equal to the deep sleep duration. We update the seconds counter if
|
|
||||||
* we have to and we also do some housekeeping so that the next second will
|
|
||||||
* increment when it is meant to.
|
|
||||||
*
|
*
|
||||||
* \note This function is only meant to be used by lpm_exit(). Applications
|
* \note This function is only meant to be used by lpm_exit(). Applications
|
||||||
* should really avoid calling this
|
* should really avoid calling this
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
clock_adjust(clock_time_t ticks)
|
clock_adjust(void)
|
||||||
{
|
{
|
||||||
/* Halt the SysTick while adjusting */
|
/* Halt the SysTick while adjusting */
|
||||||
REG(SYSTICK_STCTRL) &= ~SYSTICK_STCTRL_ENABLE;
|
REG(SYSTICK_STCTRL) &= ~SYSTICK_STCTRL_ENABLE;
|
||||||
|
|
||||||
/* Moving forward by more than a second? */
|
update_ticks();
|
||||||
secs += ticks >> 7;
|
|
||||||
|
|
||||||
/* Increment tick count */
|
|
||||||
count += ticks;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update internal second countdown so that next second change will actually
|
|
||||||
* happen when it's meant to happen.
|
|
||||||
*/
|
|
||||||
second_countdown -= ticks;
|
|
||||||
|
|
||||||
if(second_countdown == 0 || second_countdown > 128) {
|
|
||||||
secs++;
|
|
||||||
second_countdown -= 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Re-Start the SysTick */
|
/* Re-Start the SysTick */
|
||||||
REG(SYSTICK_STCTRL) |= SYSTICK_STCTRL_ENABLE;
|
REG(SYSTICK_STCTRL) |= SYSTICK_STCTRL_ENABLE;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/**
|
/**
|
||||||
* \brief The clock Interrupt Service Routine. It polls the etimer process
|
* \brief The clock Interrupt Service Routine
|
||||||
* if an etimer has expired. It also updates the software clock tick and
|
*
|
||||||
* seconds counter since reset.
|
* It polls the etimer process if an etimer has expired. It also updates the
|
||||||
|
* software clock tick and seconds counter.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
clock_isr(void)
|
clock_isr(void)
|
||||||
{
|
{
|
||||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||||
count++;
|
|
||||||
if(etimer_pending()) {
|
|
||||||
etimer_request_poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(--second_countdown == 0) {
|
update_ticks();
|
||||||
secs++;
|
|
||||||
second_countdown = CLOCK_SECOND;
|
|
||||||
}
|
|
||||||
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -88,14 +88,11 @@ rtimer_clock_t lpm_stats[3];
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/*
|
/*
|
||||||
* Remembers what time it was when went to deep sleep
|
* Remembers what time it was when went to deep sleep
|
||||||
* This is used when coming out of PM1/2 to adjust the system clock, which
|
* This is used when coming out of PM0/1/2 to keep stats
|
||||||
* stops ticking while in those PMs
|
|
||||||
*/
|
*/
|
||||||
static rtimer_clock_t sleep_enter_time;
|
static rtimer_clock_t sleep_enter_time;
|
||||||
|
|
||||||
#define RTIMER_CLOCK_TICK_RATIO (RTIMER_SECOND / CLOCK_SECOND)
|
void clock_adjust(void);
|
||||||
|
|
||||||
void clock_adjust(clock_time_t ticks);
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* Stores the currently specified MAX allowed PM */
|
/* Stores the currently specified MAX allowed PM */
|
||||||
static uint8_t max_pm;
|
static uint8_t max_pm;
|
||||||
|
@ -137,10 +134,7 @@ enter_pm0(void)
|
||||||
/* We are only interested in IRQ energest while idle or in LPM */
|
/* We are only interested in IRQ energest while idle or in LPM */
|
||||||
ENERGEST_IRQ_RESTORE(irq_energest);
|
ENERGEST_IRQ_RESTORE(irq_energest);
|
||||||
|
|
||||||
/*
|
/* Remember the current time so we can keep stats when we wake up */
|
||||||
* After PM0 we don't need to adjust the system clock. Thus, saving the time
|
|
||||||
* we enter Deep Sleep is only required if we are keeping stats.
|
|
||||||
*/
|
|
||||||
if(LPM_CONF_STATS) {
|
if(LPM_CONF_STATS) {
|
||||||
sleep_enter_time = RTIMER_NOW();
|
sleep_enter_time = RTIMER_NOW();
|
||||||
}
|
}
|
||||||
|
@ -215,10 +209,8 @@ lpm_exit()
|
||||||
RTIMER_NOW() - sleep_enter_time);
|
RTIMER_NOW() - sleep_enter_time);
|
||||||
|
|
||||||
/* Adjust the system clock, since it was not counting while we were sleeping
|
/* Adjust the system clock, since it was not counting while we were sleeping
|
||||||
* We need to convert sleep duration from rtimer ticks to clock ticks and
|
* We need to convert sleep duration from rtimer ticks to clock ticks */
|
||||||
* this will cost us some accuracy */
|
clock_adjust();
|
||||||
clock_adjust((clock_time_t)
|
|
||||||
((RTIMER_NOW() - sleep_enter_time) / RTIMER_CLOCK_TICK_RATIO));
|
|
||||||
|
|
||||||
/* Restore system clock to the 32 MHz XOSC */
|
/* Restore system clock to the 32 MHz XOSC */
|
||||||
select_32_mhz_xosc();
|
select_32_mhz_xosc();
|
||||||
|
@ -306,8 +298,10 @@ lpm_enter()
|
||||||
ENERGEST_OFF(ENERGEST_TYPE_CPU);
|
ENERGEST_OFF(ENERGEST_TYPE_CPU);
|
||||||
ENERGEST_ON(ENERGEST_TYPE_LPM);
|
ENERGEST_ON(ENERGEST_TYPE_LPM);
|
||||||
|
|
||||||
/* Remember the current time so we can adjust the clock when we wake up */
|
/* Remember the current time so we can keep stats when we wake up */
|
||||||
sleep_enter_time = RTIMER_NOW();
|
if(LPM_CONF_STATS) {
|
||||||
|
sleep_enter_time = RTIMER_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Last chance to abort entering Deep Sleep.
|
* Last chance to abort entering Deep Sleep.
|
||||||
|
|
Loading…
Reference in a new issue