mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
7eb159c2a2
The native timer is not free running, so we can't honor it's flags. But setitimer() already provides an interval option, we only have to enable it.
230 lines
4.7 KiB
C
230 lines
4.7 KiB
C
/**
|
|
* Copyright (C) 2013 Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
|
* 2015 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 cpu_native
|
|
* @ingroup drivers_periph_timer
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Native CPU periph/timer.h implementation
|
|
*
|
|
* Uses POSIX realtime clock and POSIX itimer to mimic hardware.
|
|
*
|
|
* This is based on native's hwtimer implementation by Ludwig Knüpfer.
|
|
* I removed the multiplexing, as xtimer does the same. (kaspar)
|
|
*
|
|
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#ifdef __MACH__
|
|
#include <mach/clock.h>
|
|
#include <mach/mach_init.h>
|
|
#include <mach/mach_port.h>
|
|
#include <mach/mach_host.h>
|
|
/* Both OS X and RIOT typedef thread_t. timer.c does not use either thread_t. */
|
|
#define thread_t riot_thread_t
|
|
#endif
|
|
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
|
|
#include "cpu.h"
|
|
#include "cpu_conf.h"
|
|
#include "native_internal.h"
|
|
#include "periph/timer.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define NATIVE_TIMER_SPEED 1000000
|
|
|
|
static unsigned long time_null;
|
|
|
|
static timer_cb_t _callback;
|
|
static void *_cb_arg;
|
|
|
|
static struct itimerval itv;
|
|
|
|
/**
|
|
* returns ticks for give timespec
|
|
*/
|
|
static unsigned long ts2ticks(struct timespec *tp)
|
|
{
|
|
/* TODO: check for overflow */
|
|
return(((unsigned long)tp->tv_sec * NATIVE_TIMER_SPEED) + (tp->tv_nsec / 1000));
|
|
}
|
|
|
|
/**
|
|
* native timer signal handler
|
|
*
|
|
* set new system timer, call timer interrupt handler
|
|
*/
|
|
void native_isr_timer(void)
|
|
{
|
|
DEBUG("%s\n", __func__);
|
|
|
|
_callback(_cb_arg, 0);
|
|
}
|
|
|
|
int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
|
{
|
|
(void)freq;
|
|
DEBUG("%s\n", __func__);
|
|
if (dev >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
if (freq != NATIVE_TIMER_SPEED) {
|
|
return -1;
|
|
}
|
|
|
|
/* initialize time delta */
|
|
time_null = 0;
|
|
time_null = timer_read(0);
|
|
|
|
_callback = cb;
|
|
_cb_arg = arg;
|
|
if (register_interrupt(SIGALRM, native_isr_timer) != 0) {
|
|
DEBUG("darn!\n\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_timer_set(tim_t dev, unsigned int offset, bool periodic)
|
|
{
|
|
DEBUG("%s\n", __func__);
|
|
|
|
if (offset && offset < NATIVE_TIMER_MIN_RES) {
|
|
offset = NATIVE_TIMER_MIN_RES;
|
|
}
|
|
|
|
memset(&itv, 0, sizeof(itv));
|
|
itv.it_value.tv_sec = (offset / 1000000);
|
|
itv.it_value.tv_usec = offset % 1000000;
|
|
if (periodic) {
|
|
itv.it_interval = itv.it_value;
|
|
}
|
|
|
|
DEBUG("timer_set(): setting %lu.%06lu\n", itv.it_value.tv_sec, itv.it_value.tv_usec);
|
|
|
|
timer_start(dev);
|
|
}
|
|
|
|
int timer_set(tim_t dev, int channel, unsigned int offset)
|
|
{
|
|
DEBUG("%s\n", __func__);
|
|
|
|
if (channel != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (!offset) {
|
|
offset = NATIVE_TIMER_MIN_RES;
|
|
}
|
|
|
|
do_timer_set(dev, offset, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
|
{
|
|
uint32_t now = timer_read(dev);
|
|
return timer_set(dev, channel, value - now);
|
|
}
|
|
|
|
int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags)
|
|
{
|
|
(void)flags;
|
|
|
|
if (channel != 0) {
|
|
return -1;
|
|
}
|
|
|
|
do_timer_set(dev, value, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_clear(tim_t dev, int channel)
|
|
{
|
|
(void)channel;
|
|
|
|
do_timer_set(dev, 0, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void timer_start(tim_t dev)
|
|
{
|
|
(void)dev;
|
|
DEBUG("%s\n", __func__);
|
|
|
|
_native_syscall_enter();
|
|
if (real_setitimer(ITIMER_REAL, &itv, NULL) == -1) {
|
|
err(EXIT_FAILURE, "timer_arm: setitimer");
|
|
}
|
|
_native_syscall_leave();
|
|
}
|
|
|
|
void timer_stop(tim_t dev)
|
|
{
|
|
(void)dev;
|
|
DEBUG("%s\n", __func__);
|
|
|
|
_native_syscall_enter();
|
|
struct itimerval zero = {0};
|
|
if (real_setitimer(ITIMER_REAL, &zero, &itv) == -1) {
|
|
err(EXIT_FAILURE, "timer_arm: setitimer");
|
|
}
|
|
_native_syscall_leave();
|
|
|
|
DEBUG("time left: %lu.%06lu\n", itv.it_value.tv_sec, itv.it_value.tv_usec);
|
|
}
|
|
|
|
unsigned int timer_read(tim_t dev)
|
|
{
|
|
if (dev >= TIMER_NUMOF) {
|
|
return 0;
|
|
}
|
|
|
|
struct timespec t;
|
|
|
|
DEBUG("timer_read()\n");
|
|
|
|
_native_syscall_enter();
|
|
#ifdef __MACH__
|
|
clock_serv_t cclock;
|
|
mach_timespec_t mts;
|
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
|
clock_get_time(cclock, &mts);
|
|
mach_port_deallocate(mach_task_self(), cclock);
|
|
t.tv_sec = mts.tv_sec;
|
|
t.tv_nsec = mts.tv_nsec;
|
|
#else
|
|
|
|
if (real_clock_gettime(CLOCK_MONOTONIC, &t) == -1) {
|
|
err(EXIT_FAILURE, "timer_read: clock_gettime");
|
|
}
|
|
|
|
#endif
|
|
_native_syscall_leave();
|
|
|
|
return ts2ticks(&t) - time_null;
|
|
}
|