Skip to content
Closed
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
2 changes: 1 addition & 1 deletion examples/timer_periodic_wakeup/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

int main(void)
{
uint32_t last_wakeup = xtimer_now();
uint64_t last_wakeup = xtimer_now();

@jnohlgard jnohlgard Jul 8, 2016

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of these last_wakeup changes should use xtimer_now64(). Not because I expect the startup of the example code to take more than 71 minutes, but because the examples will be copied by users who may use them in a different context than the original example was written for, where the last_wakeup may be initialized after the system has been running for 71 minutes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


while(1) {
xtimer_periodic_wakeup(&last_wakeup, INTERVAL);
Expand Down
4 changes: 2 additions & 2 deletions sys/include/xtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ static inline void xtimer_spin(uint32_t microseconds);
* @param[in] last_wakeup base time stamp for the wakeup
* @param[in] period time in microseconds that will be added to last_wakeup
*/
void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period);
void xtimer_periodic_wakeup(uint64_t *last_wakeup, uint32_t period);

/**
* @brief Set a timer that sends a message
Expand Down Expand Up @@ -407,7 +407,7 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
#if XTIMER_WIDTH != 32
#define XTIMER_MASK ((0xffffffff >> XTIMER_WIDTH) << XTIMER_WIDTH)
#else
#define XTIMER_MASK (0)
#define XTIMER_MASK (0U)
#endif

#define XTIMER_MASK_SHIFTED XTIMER_TICKS_TO_USEC(XTIMER_MASK)
Expand Down
37 changes: 24 additions & 13 deletions sys/include/xtimer/implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ extern "C" {

#if XTIMER_MASK
extern volatile uint32_t _xtimer_high_cnt;
#define XTIMER_PERIOD_LENGTH XTIMER_TICKS_TO_USEC(~XTIMER_MASK + 1)
#endif

#define XTIMER_MSBMASK (((uint32_t)1)<<(XTIMER_WIDTH - 1 + XTIMER_SHIFT))

#if (XTIMER_SHIFT < 0)
#define XTIMER_USEC_TO_TICKS(value) ( (value) << -XTIMER_SHIFT )
#define XTIMER_TICKS_TO_USEC(value) ( (value) >> -XTIMER_SHIFT )
Expand Down Expand Up @@ -71,7 +74,7 @@ static inline uint32_t _xtimer_lltimer_mask(uint32_t val)
* @brief xtimer internal stuff
* @internal
*/
int _xtimer_set_absolute(xtimer_t *timer, uint32_t target);
void _xtimer_set_absolute(xtimer_t *timer);
void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset);
void _xtimer_sleep(uint32_t offset, uint32_t long_offset);
/** @} */
Expand All @@ -89,18 +92,26 @@ void _xtimer_sleep(uint32_t offset, uint32_t long_offset);
static inline uint32_t xtimer_now(void)
{
#if XTIMER_MASK
uint32_t latched_high_cnt, now;

/* _high_cnt can change at any time, so check the value before
* and after reading the low-level timer. If it hasn't changed,
* then it can be safely applied to the timer count. */

do {
latched_high_cnt = _xtimer_high_cnt;
now = _xtimer_lltimer_now();
} while (_xtimer_high_cnt != latched_high_cnt);

return latched_high_cnt | now;
uint32_t high_cnt = _xtimer_high_cnt;
uint32_t now = _xtimer_lltimer_now();

/* _xtimer_high_cnt and the underlying hardware timer both count the most
* significant bit of the underlying hardware timer.
*
* Should the counter variable indicate we're in the first half of the
* hardware timer period, but the hardware timer indicates otherwise, the
* OR handles the missed interrupt.
*
* Should the counter variable indicate we're in the second half of the
* hardware timer period, but the actual timer value does not, we
* compensate by adding a half timer period.
*
* That way, timer overflow interrupts which are up to half a timer period
* late can be compensated for.
*/

return (high_cnt | now) +
((uint32_t)((high_cnt & (XTIMER_MSBMASK)) && (~now & XTIMER_MSBMASK))) * (XTIMER_PERIOD_LENGTH >> 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply high_cnt + now?

#else
return _xtimer_lltimer_now();
#endif
Expand Down
65 changes: 8 additions & 57 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,70 +58,21 @@ void _xtimer_sleep(uint32_t offset, uint32_t long_offset)
mutex_lock(&mutex);
}

void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
void xtimer_periodic_wakeup(uint64_t *last_wakeup, uint32_t period)
{
xtimer_t timer;
mutex_t mutex = MUTEX_INIT;
*last_wakeup += period;

timer.callback = _callback_unlock_mutex;
timer.arg = (void*) &mutex;

uint32_t target = (*last_wakeup) + period;
uint32_t now = xtimer_now();
/* make sure we're not setting a value in the past */
if (now < (*last_wakeup)) {
/* base timer overflowed between last_wakeup and now */
if (!((now < target) && (target < (*last_wakeup)))) {
/* target time has already passed */
goto out;
}
}
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out;
}
}
timer.long_target = (*last_wakeup)<<32;
timer.target = *last_wakeup;

/*
* For large offsets, set an absolute target time.
* As that might cause an underflow, for small offsets, set a relative
* target time.
* For very small offsets, spin.
*/
/*
* Note: last_wakeup _must never_ specify a time in the future after
* _xtimer_periodic_sleep returns.
* If this happens, last_wakeup may specify a time in the future when the
* next call to _xtimer_periodic_sleep is made, which in turn will trigger
* the overflow logic above and make the next timer fire too early, causing
* last_wakeup to point even further into the future, leading to a chain
* reaction.
*
* tl;dr Don't return too early!
*/
uint32_t offset = target - now;
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 "\n", now, target, offset);
if (offset < XTIMER_PERIODIC_SPIN) {
xtimer_spin(offset);
}
else {
if (offset < XTIMER_PERIODIC_RELATIVE) {
/* NB: This will overshoot the target by the amount of time it took
* to get here from the beginning of xtimer_periodic_wakeup()
*
* Since interrupts are normally enabled inside this function, this time may
* be undeterministic. */
target = xtimer_now() + offset;
}
mutex_lock(&mutex);
DEBUG("xps, abs: %" PRIu32 "\n", target);
_xtimer_set_absolute(&timer, target);
mutex_lock(&mutex);
}

out:
*last_wakeup = target;
mutex_lock(&mutex);
_xtimer_set_absolute(&timer);
mutex_lock(&mutex);
}

static void _callback_msg(void* arg)
Expand Down
Loading