Merge pull request #5612 from gebart/pr/xtimer-periodic-wakeup

xtimer: Refactor xtimer_usleep_until and rename to xtimer_periodic_wakeup
This commit is contained in:
kYc0o 2016-07-29 13:44:26 +02:00 committed by GitHub
commit 1b3012bcc6
15 changed files with 107 additions and 61 deletions

View File

@ -20,17 +20,18 @@
#include <stdio.h> #include <stdio.h>
#include "xtimer.h" #include "xtimer.h"
#include "timex.h"
/* set interval to 1 second */ /* set interval to 1 second */
#define INTERVAL (1000000U) #define INTERVAL (1U * SEC_IN_USEC)
int main(void) int main(void)
{ {
uint32_t last_wakeup = xtimer_now(); uint32_t last_wakeup = xtimer_now();
while(1) { while(1) {
xtimer_usleep_until(&last_wakeup, INTERVAL); xtimer_periodic_wakeup(&last_wakeup, INTERVAL);
printf("slept until %"PRIu32"\n", xtimer_now()); printf("slept until %" PRIu32 "\n", xtimer_now());
} }
return 0; return 0;

View File

@ -144,25 +144,24 @@ static void xtimer_nanosleep(uint32_t nanoseconds);
*/ */
static inline void xtimer_spin(uint32_t microseconds); static inline void xtimer_spin(uint32_t microseconds);
/** /**
* @brief will cause the calling thread to be suspended until the absolute * @brief will cause the calling thread to be suspended until the absolute
* time (@p last_wakeup + @p interval). * time (@p last_wakeup + @p period).
* *
* When the function returns, @p last_wakeup is set to * When the function returns, @p last_wakeup is set to
* (@p last_wakeup + @p interval). * (@p last_wakeup + @p period).
* *
* This function can be used to create periodic wakeups. * This function can be used to create periodic wakeups.
* @c last_wakeup should be set to xtimer_now() before first call of the * @c last_wakeup should be set to xtimer_now() before first call of the
* function. * function.
* *
* If the result of (@p last_wakeup + usecs) would be in the past, the function * If the result of (@p last_wakeup + @p period) would be in the past, the function
* sets @p last_wakeup to @p last_wakeup + @p interval and returns immediately. * sets @p last_wakeup to @p last_wakeup + @p period and returns immediately.
* *
* @param[in] last_wakeup base time for the wakeup * @param[in] last_wakeup base time stamp for the wakeup
* @param[in] usecs time in microseconds that will be added to * @param[in] period time in microseconds that will be added to last_wakeup
* last_wakeup
*/ */
void xtimer_usleep_until(uint32_t *last_wakeup, uint32_t usecs); void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period);
/** /**
* @brief Set a timer that sends a message * @brief Set a timer that sends a message
@ -326,6 +325,29 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
#define XTIMER_ISR_BACKOFF 20 #define XTIMER_ISR_BACKOFF 20
#endif #endif
#ifndef XTIMER_PERIODIC_SPIN
/**
* @brief xtimer_periodic_wakeup spin cutoff
*
* If the difference between target time and now is less than this value, then
* xtimer_periodic_wakeup will use xtimer_spin instead of setting a timer.
*/
#define XTIMER_PERIODIC_SPIN (XTIMER_BACKOFF * 2)
#endif
#ifndef XTIMER_PERIODIC_RELATIVE
/**
* @brief xtimer_periodic_wakeup relative target cutoff
*
* If the difference between target time and now is less than this value, then
* xtimer_periodic_wakeup will set a relative target time in the future instead
* of the true target.
*
* This is done to prevent target time underflows.
*/
#define XTIMER_PERIODIC_RELATIVE (512)
#endif
#ifndef XTIMER_SHIFT #ifndef XTIMER_SHIFT
/** /**
* @brief xtimer prescaler value * @brief xtimer prescaler value
@ -373,7 +395,6 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
#define XTIMER_WIDTH (32) #define XTIMER_WIDTH (32)
#endif #endif
#if XTIMER_WIDTH != 32
/** /**
* @brief xtimer timer mask * @brief xtimer timer mask
* *
@ -383,6 +404,7 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
* For a 16bit timer, the mask would be 0xFFFF0000, for a 24bit timer, the mask * For a 16bit timer, the mask would be 0xFFFF0000, for a 24bit timer, the mask
* would be 0xFF000000. * would be 0xFF000000.
*/ */
#if XTIMER_WIDTH != 32
#define XTIMER_MASK ((0xffffffff >> XTIMER_WIDTH) << XTIMER_WIDTH) #define XTIMER_MASK ((0xffffffff >> XTIMER_WIDTH) << XTIMER_WIDTH)
#else #else
#define XTIMER_MASK (0) #define XTIMER_MASK (0)

View File

@ -58,51 +58,67 @@ void _xtimer_sleep(uint32_t offset, uint32_t long_offset)
mutex_lock(&mutex); mutex_lock(&mutex);
} }
void xtimer_usleep_until(uint32_t *last_wakeup, uint32_t interval) { void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
xtimer_t timer; xtimer_t timer;
mutex_t mutex = MUTEX_INIT; mutex_t mutex = MUTEX_INIT;
timer.callback = _callback_unlock_mutex; timer.callback = _callback_unlock_mutex;
timer.arg = (void*) &mutex; timer.arg = (void*) &mutex;
uint32_t target = *last_wakeup + interval; uint32_t target = (*last_wakeup) + period;
uint32_t now = xtimer_now(); uint32_t now = xtimer_now();
/* make sure we're not setting a value in the past */ /* make sure we're not setting a value in the past */
if (now < *last_wakeup) { if (now < (*last_wakeup)) {
/* base timer overflowed */ /* base timer overflowed between last_wakeup and now */
if (!((target < *last_wakeup) && (target > now))) { if (!((now < target) && (target < (*last_wakeup)))) {
/* target time has already passed */
goto out; goto out;
} }
} }
else if (! ((target < *last_wakeup) || (target > now))) { else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out; goto out;
} }
}
/* For large offsets, set an absolute target time, as /*
* it is more exact. * For large offsets, set an absolute target time.
* * As that might cause an underflow, for small offsets, set a relative
* As that might cause an underflow, for small offsets, * target time.
* use a relative offset, as that can never underflow.
*
* For very small offsets, spin. * 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; uint32_t offset = target - now;
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 "\n", now, target, offset);
if (offset > (XTIMER_BACKOFF * 2)) { if (offset < XTIMER_PERIODIC_SPIN) {
mutex_lock(&mutex);
if (offset >> 9) { /* >= 512 */
offset = target;
}
else {
offset += xtimer_now();
}
_xtimer_set_absolute(&timer, offset);
mutex_lock(&mutex);
}
else {
xtimer_spin(offset); 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: out:
*last_wakeup = target; *last_wakeup = target;

View File

@ -40,7 +40,7 @@ int main(void)
while(1) { while(1) {
uint16_t val = bh1750fvi_sample(&dev); uint16_t val = bh1750fvi_sample(&dev);
printf("value: %5i lux\n", (int)val); printf("value: %5i lux\n", (int)val);
xtimer_usleep_until(&last, RATE); xtimer_periodic_wakeup(&last, RATE);
} }
return 0; return 0;

View File

@ -27,6 +27,7 @@
#include "shell.h" #include "shell.h"
#include "xtimer.h" #include "xtimer.h"
#include "timex.h"
#include "srf02.h" #include "srf02.h"
#ifndef TEST_SRF02_I2C #ifndef TEST_SRF02_I2C
@ -36,7 +37,7 @@
#error "TEST_MODE not defined" #error "TEST_MODE not defined"
#endif #endif
#define SAMPLE_PERIOD (100LU * 1000U) #define SAMPLE_PERIOD (100LU * MS_IN_USEC) /* 100 ms */
static srf02_t dev; static srf02_t dev;
@ -78,7 +79,7 @@ static int cmd_sample(int argc, char **argv)
while(1) { while(1) {
sample(); sample();
xtimer_usleep_until(&wakeup, SAMPLE_PERIOD); xtimer_periodic_wakeup(&wakeup, SAMPLE_PERIOD);
} }
return 0; return 0;

View File

@ -21,11 +21,12 @@
#include <stdio.h> #include <stdio.h>
#include "xtimer.h" #include "xtimer.h"
#include "timex.h"
#include "periph/adc.h" #include "periph/adc.h"
#define RES ADC_RES_10BIT #define RES ADC_RES_10BIT
#define DELAY (100LU * 1000U) #define DELAY (100LU * MS_IN_USEC) /* 100 ms */
int main(void) int main(void)
@ -56,7 +57,7 @@ int main(void)
printf("ADC_LINE(%i): %i\n", i, sample); printf("ADC_LINE(%i): %i\n", i, sample);
} }
} }
xtimer_usleep_until(&last, DELAY); xtimer_periodic_wakeup(&last, DELAY);
} }
return 0; return 0;

View File

@ -56,7 +56,7 @@ int main(void)
dac_set(DAC_LINE(i), val); dac_set(DAC_LINE(i), val);
} }
val += step; val += step;
xtimer_usleep_until(&last, DELAY); xtimer_periodic_wakeup(&last, DELAY);
} }
return 0; return 0;

View File

@ -27,9 +27,10 @@
#include <stdio.h> #include <stdio.h>
#include "xtimer.h" #include "xtimer.h"
#include "timex.h"
#include "periph/pwm.h" #include "periph/pwm.h"
#define INTERVAL (10000U) #define INTERVAL (10LU * MS_IN_USEC) /* 10 ms */
#define STEP (10) #define STEP (10)
#define MODE PWM_LEFT #define MODE PWM_LEFT
@ -71,7 +72,7 @@ int main(void)
step = -step; step = -step;
} }
xtimer_usleep_until(&last_wakeup, INTERVAL); xtimer_periodic_wakeup(&last_wakeup, INTERVAL);
} }
return 0; return 0;

View File

@ -26,7 +26,7 @@
/** /**
* @brief Read th sensors every second * @brief Read th sensors every second
*/ */
#define INTERVAL (1000000LU) #define INTERVAL (1LU * SEC_IN_USEC)
int main(void) int main(void)
@ -50,7 +50,7 @@ int main(void)
dev = dev->next; dev = dev->next;
} }
xtimer_usleep_until(&last, INTERVAL); xtimer_periodic_wakeup(&last, INTERVAL);
} }
return 0; return 0;

View File

@ -192,7 +192,7 @@ int main(void)
uint32_t last_wakeup = xtimer_now(); uint32_t last_wakeup = xtimer_now();
while (1) { while (1) {
xtimer_usleep_until(&last_wakeup, TEST_INTERVAL); xtimer_periodic_wakeup(&last_wakeup, TEST_INTERVAL);
msg_try_send(&m, pid3); msg_try_send(&m, pid3);
} }
} }

View File

@ -19,7 +19,7 @@ we consider this long-term...). The mid-term timers are set to 3 and 5 minutes.
Both kind of timers have one that is using `xtimer_usleep` and one that is Both kind of timers have one that is using `xtimer_usleep` and one that is
using `xtimer_set_msg`. using `xtimer_set_msg`.
The short-term timer is triggered every 50ms and is using `xtimer_sleep_until`. The short-term timer is triggered every 50ms and is using `xtimer_periodic_wakeup`.
Each time this timer triggers, it increments a software counter, which triggers Each time this timer triggers, it increments a software counter, which triggers
then a message every minute. A 50ms interval should be small enough, to trigger then a message every minute. A 50ms interval should be small enough, to trigger
also for 16-bit wide timers at least once in every timer period. also for 16-bit wide timers at least once in every timer period.
@ -28,4 +28,4 @@ On each mid- and long-term timer event, the output shows also the number of
fast timer (1min timer) ticks, that have been triggered since a timer was fast timer (1min timer) ticks, that have been triggered since a timer was
triggered last. This number should be equal to the timers interval. triggered last. This number should be equal to the timers interval.
For reasonable results, you should run this test at least for some ours... For reasonable results, you should run this test at least for some hours...

View File

@ -113,7 +113,7 @@ void *ticker(void *arg)
msg_send(&msg, print_pid); msg_send(&msg, print_pid);
} }
xtimer_usleep_until(&base, INT_SHORT); xtimer_periodic_wakeup(&base, INT_SHORT);
} }
return NULL; return NULL;

View File

@ -30,23 +30,25 @@ int32_t res[NUMOF];
int main(void) int main(void)
{ {
puts("xtimer_usleep_until test application.\n"); puts("xtimer_periodic_wakeup test application.\n");
uint32_t interval = NUMOF; uint32_t interval = NUMOF;
int32_t max_diff = INT32_MIN; int32_t max_diff = INT32_MIN;
int32_t min_diff = INT32_MAX; int32_t min_diff = INT32_MAX;
for (int i = 0; i < NUMOF; i++) { for (int i = 0; i < NUMOF; i++) {
printf("Testing interval %u... (now=%"PRIu32")\n", (unsigned)interval, xtimer_now()); uint32_t now = xtimer_now();
printf("Testing interval %" PRIu32 "... (now=%" PRIu32 ")\n", interval, now);
uint32_t last_wakeup = xtimer_now(); uint32_t last_wakeup = xtimer_now();
uint32_t before = last_wakeup; uint32_t before = last_wakeup;
xtimer_usleep_until(&last_wakeup, (unsigned)interval); xtimer_periodic_wakeup(&last_wakeup, interval);
res[i] = (xtimer_now()-before)-interval; now = xtimer_now();
res[i] = (now - before) - interval;
interval -= 1; interval -= 1;
} }
for (int i = 0; i < NUMOF; i++) { for (int i = 0; i < NUMOF; i++) {
printf("%4d diff=%"PRIi32"\n", NUMOF-i, res[i]); printf("%4d diff=%" PRIi32 "\n", NUMOF - i, res[i]);
if (res[i] > max_diff) { if (res[i] > max_diff) {
max_diff = res[i]; max_diff = res[i];

View File

@ -18,6 +18,8 @@
* @} * @}
*/ */
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include "thread.h" #include "thread.h"
@ -46,11 +48,11 @@ int main(void)
xtimer_set_wakeup(&xtimer, 200000, me); xtimer_set_wakeup(&xtimer, 200000, me);
xtimer_set_wakeup(&xtimer2, 100000, me); xtimer_set_wakeup(&xtimer2, 100000, me);
printf("now=%u\n", (unsigned)xtimer_now()); printf("now=%" PRIu32 "\n", xtimer_now());
thread_sleep(); thread_sleep();
printf("now=%u\n", (unsigned)xtimer_now()); printf("now=%" PRIu32 "\n", xtimer_now());
thread_sleep(); thread_sleep();
printf("now=%u\n", (unsigned)xtimer_now()); printf("now=%" PRIu32 "\n", xtimer_now());
printf("Test completed!\n"); printf("Test completed!\n");