Skip to content
Open
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
4 changes: 4 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,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)))
Expand Down
1 change: 1 addition & 0 deletions sys/evtimer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base
202 changes: 202 additions & 0 deletions sys/evtimer/evtimer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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>
*
* @}
*/
#include "evtimer.h"
#include "div.h"
#include "irq.h"
#include "msg.h"
#include "xtimer.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

static void _add_event_to_list(evtimer_event_t *list, evtimer_event_t *event)
{
uint32_t delta_sum = 0;

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_event_t *list, evtimer_event_t *event)
{
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(),
(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_now64();
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);
_add_event_to_list(evtimer->events, event);

if (evtimer->events == event) {
_set_timer(&evtimer->timer, event->offset);
}
irq_restore(state);
}

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->events, event);
_update_timer(evtimer);
irq_restore(state);
}

bool evtimer_is_event_sched(evtimer_t *evtimer, evtimer_event_t *event)
{
evtimer_event_t *list_entry = evtimer->events;
while (list_entry) {
if (list_entry == event) {
return true;
}
list_entry = list_entry->next;
}
return false;
}

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;
}
}

void evtimer_msg_handler(void *arg)
{
DEBUG("evtimer_msg_handler()\n");

evtimer_t *evtimer = (evtimer_t *) arg;

evtimer_event_t *event = evtimer->events;
event->offset = 0;

evtimer_msg_event_t *mevent;
while ((mevent = (evtimer_msg_event_t *)_get_next(evtimer))) {
msg_send_int(&mevent->msg, mevent->msg.sender_pid);
}

_update_timer(evtimer);

if (!irq_is_in()) {
thread_yield_higher();
}
}

void evtimer_init(evtimer_t *evtimer, void (*handler)(void *))
{
evtimer->timer.callback = 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;
}
}
27 changes: 27 additions & 0 deletions sys/include/div.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,33 @@ static inline uint64_t div_u64_by_15625(uint64_t val)
return (val * 0x431bde83UL) >> (12 + 32);
}

/**
* @brief Integer divide val by 125
*
* This function can be used to convert uint64_t microsecond times (or
* intervals) to miliseconds and store them in uint32_t variables, with up to
* ~50 days worth of miliseconds ((2**32*1000) -1).
* Use e.g., ms = div_u64_by_125(microseconds >> 3)
*
* @pre val <= 536870911999 ((2**32 * 125) -1)
*
* @param[in] val dividend
* @return (val / 125)
*/
static inline uint32_t div_u64_by_125(uint64_t val)
{
/* a higher value would overflow the result type */
assert(val <= 536870911999LLU);

uint32_t hi = val >> 32;
uint32_t lo = val;
uint32_t r = (lo >> 16) + (hi << 16);
uint32_t res = r / 125;
r = ((r % 125) << 16) + (lo & 0xFFFF);
res = (res << 16) + r / 125;
return res;
}

/**
* @brief Integer divide val by 1000000
*
Expand Down
81 changes: 81 additions & 0 deletions sys/include/evtimer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 timers
* @ingroup sys
* @brief Provides timers for events up to 2**32 milliseconds in the future
*
* RIOT's main timer subsystem is xtimer, but for many applications xtimer's
* 64bit absolute time values are wasteful or clumsy to use.
*
* Compared to xtimer, evtimer offers:
*
* - only relative 32bit 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 xtimer!
*
* - more flexible, "intrusive" timer type
* evtimer_event_t only contains the necessary fields, which can be extended
* as needed, and handlers define actions taken on timer triggers.
* Check out evtimer_msg_event as example.
*
* - uses xtimer as backend
*
* @{
*
* @file
* @brief evtimer API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/

#ifndef EVTIMER_H
#define EVTIMER_H

#include <stdint.h>

#include "msg.h"
#include "xtimer.h"

#ifdef __cplusplus
extern "C" {
#endif

/** forward declaration */
typedef struct evtimer_event evtimer_event_t;

typedef struct {
xtimer_t timer;
evtimer_event_t *events;
} evtimer_t;

struct evtimer_event {
evtimer_event_t *next;
uint32_t offset;
};

typedef struct {
evtimer_event_t event;
msg_t msg;
} evtimer_msg_event_t;

void evtimer_init(evtimer_t *evtimer, void(*handler)(void*));
void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event);
void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event);
bool evtimer_is_event_sched(evtimer_t *evtimer, evtimer_event_t *event);
void evtimer_msg_handler(void *arg);
void evtimer_print(const evtimer_t *evtimer);

#ifdef __cplusplus
}
#endif

#endif /* EVTIMER_H */
/** @} */