-
Notifications
You must be signed in to change notification settings - Fork 2.1k
xtimer: Add xtimer_periodic_msg #7496
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
Changes from all commits
54812f1
c60130b
0fb69ce
ecf2cfe
ca6ad8b
b9ee7a7
129b6a9
e707a7e
52fce74
e996db1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| /* | ||
| * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> | ||
| * Copyright (C) 2016 Eistec AB | ||
| * Copyright (C) 2016-2017 Eistec AB | ||
| * | ||
| * This file is subject to the terms and conditions of the GNU Lesser | ||
| * General Public License v2.1. See the file LICENSE in the top level | ||
|
|
@@ -58,97 +58,109 @@ void _xtimer_tsleep(uint32_t offset, uint32_t long_offset) | |
| } | ||
|
|
||
| xtimer_t timer; | ||
| mutex_t mutex = MUTEX_INIT; | ||
| mutex_t mutex = MUTEX_INIT_LOCKED; | ||
|
|
||
| timer.callback = _callback_unlock_mutex; | ||
| timer.arg = (void*) &mutex; | ||
| timer.target = timer.long_target = 0; | ||
|
|
||
| mutex_lock(&mutex); | ||
| _xtimer_set64(&timer, offset, long_offset); | ||
| mutex_lock(&mutex); | ||
| } | ||
|
|
||
| void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) { | ||
| xtimer_t timer; | ||
| mutex_t mutex = MUTEX_INIT; | ||
|
|
||
| timer.callback = _callback_unlock_mutex; | ||
| timer.arg = (void*) &mutex; | ||
|
|
||
| void _xtimer_periodic(xtimer_t *timer, uint32_t *last_wakeup, uint32_t period) | ||
| { | ||
| 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; | ||
| do { | ||
| /* 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 */ | ||
| timer->callback(timer->arg); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| /* base timer did not overflow */ | ||
| if ((((*last_wakeup) <= target) && (target <= now))) { | ||
| /* target time has already passed */ | ||
| goto out; | ||
| else { | ||
| /* base timer did not overflow */ | ||
| if ((((*last_wakeup) <= target) && (target <= now))) { | ||
| /* target time has already passed */ | ||
| timer->callback(timer->arg); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * 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; | ||
| /* | ||
| * 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); | ||
| timer->callback(timer->arg); | ||
| } | ||
| mutex_lock(&mutex); | ||
| DEBUG("xps, abs: %" PRIu32 "\n", target); | ||
| _xtimer_set_absolute(&timer, target); | ||
| mutex_lock(&mutex); | ||
| } | ||
| out: | ||
| 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 nondeterministic. */ | ||
| target = _xtimer_now() + offset; | ||
| } | ||
| DEBUG("xps, abs: %" PRIu32 "\n", target); | ||
| _xtimer_set_absolute(timer, target); | ||
| } | ||
| } while (0); | ||
| *last_wakeup = target; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a problem for the _msg variant, the last wakeup time will increment before that time has actually passed, leading to the situation described above in the long comment about not returning early. |
||
| } | ||
|
|
||
| void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) { | ||
| xtimer_t timer; | ||
| mutex_t mutex = MUTEX_INIT_LOCKED; | ||
|
|
||
| timer.callback = _callback_unlock_mutex; | ||
| timer.arg = &mutex; | ||
| _xtimer_periodic(&timer, last_wakeup, period); | ||
| mutex_lock(&mutex); | ||
| } | ||
|
|
||
| static void _callback_msg(void* arg) | ||
| { | ||
| msg_t *msg = (msg_t*)arg; | ||
| msg_t *msg = arg; | ||
| msg_send_int(msg, msg->sender_pid); | ||
| } | ||
|
|
||
| static inline void _setup_msg(xtimer_t *timer, msg_t *msg, kernel_pid_t target_pid) | ||
| { | ||
| timer->callback = _callback_msg; | ||
| timer->arg = (void*) msg; | ||
| timer->arg = msg; | ||
|
|
||
| /* use sender_pid field to get target_pid into callback function */ | ||
| msg->sender_pid = target_pid; | ||
| } | ||
|
|
||
| void _xtimer_periodic_msg(xtimer_t *timer, uint32_t *last_wakeup, uint32_t period, msg_t *msg, kernel_pid_t target_pid) | ||
| { | ||
| _setup_msg(timer, msg, target_pid); | ||
| _xtimer_periodic(timer, last_wakeup, period); | ||
| } | ||
|
|
||
| void _xtimer_set_msg(xtimer_t *timer, uint32_t offset, msg_t *msg, kernel_pid_t target_pid) | ||
| { | ||
| _setup_msg(timer, msg, target_pid); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| APPLICATION = xtimer_periodic_msg | ||
| include ../Makefile.tests_common | ||
|
|
||
| USEMODULE += xtimer | ||
|
|
||
| include $(RIOTBASE)/Makefile.include |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /* | ||
| * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> | ||
| * Copyright (C) 2017 Eistec AB | ||
| * | ||
| * This file is subject to the terms and conditions of the GNU Lesser | ||
| * General Public License v2.1. See the file LICENSE in the top level | ||
| * directory for more details. | ||
| */ | ||
|
|
||
| /** | ||
| * @ingroup tests | ||
| * @{ | ||
| * | ||
| * @file | ||
| * @brief timer test application | ||
| * | ||
| * @author Kaspar Schleiser <kaspar@schleiser.de> | ||
| * @author Joakim Nohlgård <joakim.nohlgard@eistec.se> | ||
| * | ||
| * @} | ||
| */ | ||
|
|
||
| #include <stdio.h> | ||
| #include "thread.h" | ||
| #include "msg.h" | ||
| #include "xtimer.h" | ||
|
|
||
| #ifndef NUMOF | ||
| #define NUMOF (256) | ||
| #endif | ||
|
|
||
| int32_t res[NUMOF]; | ||
|
|
||
| int main(void) | ||
| { | ||
| puts("xtimer_periodic_msg test application.\n"); | ||
| msg_t msg_q[1]; | ||
| msg_init_queue(msg_q, 1); | ||
|
|
||
| uint32_t interval = NUMOF; | ||
| int32_t max_diff = INT32_MIN; | ||
| int32_t min_diff = INT32_MAX; | ||
|
|
||
| for (int i = 0; i < NUMOF; i++) { | ||
| xtimer_ticks32_t now = xtimer_now(); | ||
| printf("Testing interval %" PRIu32 "... (now=%" PRIu32 ")\n", interval, xtimer_usec_from_ticks(now)); | ||
| xtimer_ticks32_t last_wakeup = xtimer_now(); | ||
| xtimer_ticks32_t before = last_wakeup; | ||
| xtimer_t timer = { .target = 0, .long_target = 0 }; | ||
| msg_t msg_out = { .type = 0xffff - interval, .content.value = interval }; | ||
| xtimer_periodic_msg(&timer, &last_wakeup, interval, &msg_out, thread_getpid()); | ||
| msg_t msg_in; | ||
| msg_receive(&msg_in); | ||
| now = xtimer_now(); | ||
| assert(msg_in.type == msg_out.type); | ||
| assert(msg_in.content.value == msg_out.content.value); | ||
| res[i] = (xtimer_usec_from_ticks(now) - xtimer_usec_from_ticks(before)) - interval; | ||
| interval -= 1; | ||
| } | ||
|
|
||
| for (int i = 0; i < NUMOF; i++) { | ||
| printf("%4d diff=%" PRIi32 "\n", NUMOF - i, res[i]); | ||
|
|
||
| if (res[i] > max_diff) { | ||
| max_diff = res[i]; | ||
| } | ||
| if (res[i] < min_diff) { | ||
| min_diff = res[i]; | ||
| } | ||
| } | ||
| printf("\nMin/max error: %" PRId32 "/%" PRId32 "\n", min_diff, max_diff); | ||
|
|
||
| if (min_diff < -1000 || max_diff > 1000) { | ||
| puts("too large difference.\n"); | ||
| puts("Test Failed.\n"); | ||
| return 1; | ||
| } | ||
|
|
||
| printf("\nTest complete.\n"); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,6 @@ | ||
| APPLICATION = xtimer_periodic_wakeup | ||
| include ../Makefile.tests_common | ||
|
|
||
| BOARD_INSUFFICIENT_MEMORY := chronos | ||
|
|
||
| USEMODULE += xtimer | ||
|
|
||
| include $(RIOTBASE)/Makefile.include |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need the
do { ... } while(0);?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that the break replaces the goto