evtimer: initial import
This commit is contained in:
parent
8c0249dc56
commit
fce919d27c
@ -580,6 +580,10 @@ ifneq (,$(filter phydat,$(USEMODULE)))
|
||||
USEMODULE += fmt
|
||||
endif
|
||||
|
||||
ifneq (,$(filter evtimer,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter random,$(USEMODULE)))
|
||||
# select default prng
|
||||
ifeq (,$(filter prng_%,$(USEMODULE)))
|
||||
|
||||
1
sys/evtimer/Makefile
Normal file
1
sys/evtimer/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
205
sys/evtimer/evtimer.c
Normal file
205
sys/evtimer/evtimer.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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 sys_evtimer
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief event timer implementation
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "div.h"
|
||||
#include "irq.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "evtimer.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* XXX this function is intentionally non-static, since the optimizer can't
|
||||
* handle the pointer hack in this function */
|
||||
void evtimer_add_event_to_list(evtimer_t *evtimer, evtimer_event_t *event)
|
||||
{
|
||||
uint32_t delta_sum = 0;
|
||||
|
||||
/* we want list->next to point to the first list element. thus we take the
|
||||
* *address* of evtimer->events, then cast it from (evtimer_event_t **) to
|
||||
* (evtimer_event_t*). After that, list->next actually equals
|
||||
* evtimer->events. */
|
||||
evtimer_event_t *list = (evtimer_event_t *)&evtimer->events;
|
||||
|
||||
while (list->next) {
|
||||
evtimer_event_t *list_entry = list->next;
|
||||
if ((list_entry->offset + delta_sum) > event->offset) {
|
||||
break;
|
||||
}
|
||||
delta_sum += list_entry->offset;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
event->next = list->next;
|
||||
if (list->next) {
|
||||
evtimer_event_t *next_entry = list->next;
|
||||
next_entry->offset += delta_sum;
|
||||
next_entry->offset -= event->offset;
|
||||
}
|
||||
event->offset -= delta_sum;
|
||||
|
||||
list->next = event;
|
||||
}
|
||||
|
||||
static void _del_event_from_list(evtimer_t *evtimer, evtimer_event_t *event)
|
||||
{
|
||||
evtimer_event_t *list = (evtimer_event_t *) &evtimer->events;
|
||||
|
||||
while (list->next) {
|
||||
evtimer_event_t *list_entry = list->next;
|
||||
if (list_entry == event) {
|
||||
list->next = event->next;
|
||||
if (list->next) {
|
||||
list_entry = list->next;
|
||||
list_entry->offset += event->offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void _set_timer(xtimer_t *timer, uint32_t offset)
|
||||
{
|
||||
uint64_t offset_in_us = (uint64_t)offset * 1000;
|
||||
|
||||
DEBUG("evtimer: now=%" PRIu32 " setting xtimer to %" PRIu32 ":%" PRIu32 "\n",
|
||||
xtimer_now_usec(), (uint32_t)(offset_in_us >> 32),
|
||||
(uint32_t)(offset_in_us));
|
||||
_xtimer_set64(timer, offset_in_us, offset_in_us >> 32);
|
||||
}
|
||||
|
||||
static void _update_timer(evtimer_t *evtimer)
|
||||
{
|
||||
if (evtimer->events) {
|
||||
evtimer_event_t *event = evtimer->events;
|
||||
_set_timer(&evtimer->timer, event->offset);
|
||||
}
|
||||
else {
|
||||
xtimer_remove(&evtimer->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t _get_offset(xtimer_t *timer)
|
||||
{
|
||||
uint64_t now = xtimer_now_usec64();
|
||||
uint64_t target = ((uint64_t)timer->long_target) << 32 | timer->target;
|
||||
|
||||
if (target <= now) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
target -= now;
|
||||
/* add half of 125 so integer division rounds to nearest */
|
||||
return div_u64_by_125((target >> 3) + 62);
|
||||
}
|
||||
}
|
||||
|
||||
static void _update_head_offset(evtimer_t *evtimer)
|
||||
{
|
||||
if (evtimer->events) {
|
||||
evtimer_event_t *event = evtimer->events;
|
||||
event->offset = _get_offset(&evtimer->timer);
|
||||
DEBUG("evtimer: _update_head_offset(): new head offset %" PRIu32 "\n", event->offset);
|
||||
}
|
||||
}
|
||||
|
||||
void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
|
||||
DEBUG("evtimer_add(): adding event with offset %" PRIu32 "\n", event->offset);
|
||||
|
||||
_update_head_offset(evtimer);
|
||||
evtimer_add_event_to_list(evtimer, event);
|
||||
if (evtimer->events == event) {
|
||||
_set_timer(&evtimer->timer, event->offset);
|
||||
}
|
||||
irq_restore(state);
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield_higher();
|
||||
}
|
||||
}
|
||||
|
||||
void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
|
||||
DEBUG("evtimer_del(): removing event with offset %" PRIu32 "\n", event->offset);
|
||||
|
||||
_update_head_offset(evtimer);
|
||||
_del_event_from_list(evtimer, event);
|
||||
_update_timer(evtimer);
|
||||
irq_restore(state);
|
||||
}
|
||||
|
||||
static evtimer_event_t *_get_next(evtimer_t *evtimer)
|
||||
{
|
||||
evtimer_event_t *event = evtimer->events;
|
||||
|
||||
if (event && (event->offset == 0)) {
|
||||
evtimer->events = event->next;
|
||||
return event;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _evtimer_handler(void *arg)
|
||||
{
|
||||
DEBUG("_evtimer_handler()\n");
|
||||
|
||||
evtimer_t *evtimer = (evtimer_t *)arg;
|
||||
|
||||
/* this function gets called directly by xtimer if the set xtimer expired.
|
||||
* Thus the offset of the first event is down to zero. */
|
||||
evtimer_event_t *event = evtimer->events;
|
||||
event->offset = 0;
|
||||
|
||||
/* iterate the event list */
|
||||
while ((event = _get_next(evtimer))) {
|
||||
evtimer->callback(event);
|
||||
}
|
||||
|
||||
_update_timer(evtimer);
|
||||
}
|
||||
|
||||
void evtimer_init(evtimer_t *evtimer, evtimer_callback_t handler)
|
||||
{
|
||||
evtimer->callback = handler;
|
||||
evtimer->timer.callback = _evtimer_handler;
|
||||
evtimer->timer.arg = (void *)evtimer;
|
||||
evtimer->events = NULL;
|
||||
}
|
||||
|
||||
void evtimer_print(const evtimer_t *evtimer)
|
||||
{
|
||||
evtimer_event_t *list = evtimer->events;
|
||||
|
||||
while (list->next) {
|
||||
evtimer_event_t *list_entry = list->next;
|
||||
printf("ev offset=%u\n", (unsigned)list_entry->offset);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
113
sys/include/evtimer.h
Normal file
113
sys/include/evtimer.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_evtimer Millisecond interval event timers
|
||||
* @ingroup sys
|
||||
* @brief Provides timers for events up to @$2^{32}@$ milliseconds in the
|
||||
* future
|
||||
*
|
||||
* @note Experimental and likely to replaced with unified timer API
|
||||
*
|
||||
* RIOT's main timer subsystem is @ref sys_xtimer "xtimer", but for many
|
||||
* applications @ref sys_xtimer "xtimer's" 64-bit absolute time values are
|
||||
* wasteful or clumsy to use.
|
||||
*
|
||||
* Compared to @ref sys_xtimer "xtimer", evtimer offers:
|
||||
*
|
||||
* - only relative 32-bit millisecond timer values
|
||||
* Events can be scheduled with a relative offset of up to ~49.7 days in the
|
||||
* future.
|
||||
* **For time-critical stuff, use @ref sys_xtimer "xtimer"!**
|
||||
* - more flexible, "intrusive" timer type @ref evtimer_event_t only contains
|
||||
* the necessary fields, which can be extended as needed, and handlers define
|
||||
* actions taken on timer triggers. Check out @ref evtimer_msg_event_t as
|
||||
* example.
|
||||
* - uses @ref sys_xtimer "xtimer" as backend
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief evtimer API definitions
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef EVTIMER_H
|
||||
#define EVTIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "xtimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Generic event
|
||||
*/
|
||||
typedef struct evtimer_event {
|
||||
struct evtimer_event *next; /**< the next event in the queue */
|
||||
uint32_t offset; /**< offset in milliseconds from previous event */
|
||||
} evtimer_event_t;
|
||||
|
||||
/**
|
||||
* @brief Event timer callback type
|
||||
*/
|
||||
typedef void(*evtimer_callback_t)(evtimer_event_t* event);
|
||||
|
||||
/**
|
||||
* @brief Event timer
|
||||
*/
|
||||
typedef struct {
|
||||
xtimer_t timer; /**< Timer */
|
||||
evtimer_callback_t callback; /**< Handler function for this evtimer's
|
||||
event type */
|
||||
evtimer_event_t *events; /**< Event queue */
|
||||
} evtimer_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes an event timer
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
* @param[in] handler An event handler function
|
||||
*/
|
||||
void evtimer_init(evtimer_t *evtimer, evtimer_callback_t handler);
|
||||
|
||||
/**
|
||||
* @brief Adds event to an event timer
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
* @param[in] event An event
|
||||
*/
|
||||
void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event);
|
||||
|
||||
/**
|
||||
* @brief Removes an event from an event timer
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
* @param[in] event An event
|
||||
*/
|
||||
void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event);
|
||||
|
||||
/**
|
||||
* @brief Print overview of current state of an event timer
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
*/
|
||||
void evtimer_print(const evtimer_t *evtimer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EVTIMER_H */
|
||||
/** @} */
|
||||
88
sys/include/evtimer_msg.h
Normal file
88
sys/include/evtimer_msg.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup sys_evtimer
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief IPC-based evtimer definitions
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef EVTIMER_MSG_H
|
||||
#define EVTIMER_MSG_H
|
||||
|
||||
#include "msg.h"
|
||||
#include "evtimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief IPC-message event timer
|
||||
* @extends evtimer_t
|
||||
*/
|
||||
typedef evtimer_t evtimer_msg_t;
|
||||
|
||||
/**
|
||||
* @brief IPC-message event
|
||||
* @extends evtimer_event_t
|
||||
*/
|
||||
typedef struct {
|
||||
evtimer_event_t event; /**< base class */
|
||||
msg_t msg; /**< the IPC message to generate on event */
|
||||
} evtimer_msg_event_t;
|
||||
|
||||
/**
|
||||
* @brief Adds event to an event timer that handles events via IPC
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
* @param[in] event An event
|
||||
* @param[in] target_pid The PID of the thread that should receive the IPC
|
||||
* message
|
||||
*/
|
||||
static inline void evtimer_add_msg(evtimer_msg_t *evtimer,
|
||||
evtimer_msg_event_t *event,
|
||||
kernel_pid_t target_pid)
|
||||
{
|
||||
/* use sender_pid field to get target_pid into callback function */
|
||||
event->msg.sender_pid = target_pid;
|
||||
evtimer_add(evtimer, &event->event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Event handler for IPC messages
|
||||
*
|
||||
* @param[in] event The event to handle
|
||||
*/
|
||||
static inline void _evtimer_msg_handler(evtimer_event_t *event)
|
||||
{
|
||||
evtimer_msg_event_t *mevent = (evtimer_msg_event_t *)event;
|
||||
msg_send_int(&mevent->msg, mevent->msg.sender_pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes event timer to handle events via IPC
|
||||
*
|
||||
* @param[in] evtimer An event timer
|
||||
*/
|
||||
static inline void evtimer_init_msg(evtimer_t *evtimer)
|
||||
{
|
||||
evtimer_init(evtimer, _evtimer_msg_handler);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EVTIMER_MSG_H */
|
||||
/** @} */
|
||||
Loading…
x
Reference in New Issue
Block a user