Merge pull request #15053 from kaspar030/ztimer_periodic

sys/ztimer: add periodic timers API
This commit is contained in:
Kaspar Schleiser 2020-09-29 23:58:51 +02:00 committed by GitHub
commit aa6e64316c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 338 additions and 1 deletions

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 2020 Freie Universität Berlin
* 2020 Inria
*
* 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_ztimer
* @brief Periodic ztimer API
*
* Once started, the periodic timer will call the configured callback function
* once each interval until the timer is either stopped using
* ztimer_periodic_stop or the callback function returns a non-zero value.
*
* Should the timer underflow ((time_at_interrupt + interval) % 2**32 >
* interval), the next timer will be scheduled with an offset of zero, thus
* fire right away. This leads to a callback for each missed tick, until the
* original period can be honoured again.
*
* Example:
*
* ```
* #include "ztimer/periodic.h"
*
* static void callback(void *arg)
* {
* puts(arg);
* }
*
*
* int main(void)
* {
* // allocate timer in .bss with static, so that timer can keep
* // running when leaving function scope
* static ztimer_periodic_t timer;
* // initialize timer
* ztimer_periodic_init(ZTIMER_SEC, &timer, callback, "test", 1);
*
* // start timer
* ztimer_periodic_start(&timer);
* // timer will tick every 1 second
*
* // call again to reset timer (will abort current period and trigger next
* // after full interval)
* ztimer_periodic_start(&timer);
*
* // stop timer (will not trigger anymore)
* ztimer_periodic_stop(&timer);
*
* // NOTE: if using a stack allocated timer, *it must be stopped before
* // going out of scope*!
* }
* ```
*
* @{
*
* @file
* @brief ztimer periodic API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef ZTIMER_PERIODIC_H
#define ZTIMER_PERIODIC_H
#include <stdint.h>
#include "ztimer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Periodic timer stop unless it returns this value
*/
#define ZTIMER_PERIODIC_KEEP_GOING 0
/**
* @brief ztimer periodic structure
*/
typedef struct {
ztimer_t timer; /**< timer object used for this timer */
ztimer_clock_t *clock; /**< clock for this timer */
uint32_t interval; /**< interval of this timer */
ztimer_now_t last; /**< last trigger time */
int (*callback)(void *); /**< called on each trigger */
void *arg; /**< argument for callback */
} ztimer_periodic_t;
/**
* @brief Initialize a periodic timer structure
*
* This sets up the underlying structure of a periodic timer.
* After initializing, use @ref ztimer_periodic_start() to start the timer.
*
* @param[in] clock the clock to configure this timer on
* @param[inout] timer periodic timer object to initialize
* @param[in] callback function to call on each trigger
* @param[in] arg argument to pass to callback function
* @param[in] interval period length of this timer instance
*/
void ztimer_periodic_init(ztimer_clock_t *clock, ztimer_periodic_t *timer,
int (*callback)(void *),
void *arg, uint32_t interval);
/**
* @brief Start or restart a periodic timer
*
* When called on a newly initialized timer, the timer will start.
*
* When called on an already running timer, the current interval is reset to its
* start (thus the next callback will be called after the configured interval
* has passed).
*
* @param[in] timer periodic timer object to work on
*/
void ztimer_periodic_start(ztimer_periodic_t *timer);
/**
* @brief Stop a periodic timer
*
* The periodic timer will not trigger anymore.
*
* @param[in] timer periodic timer object to stop
*/
void ztimer_periodic_stop(ztimer_periodic_t *timer);
#ifdef __cplusplus
}
#endif
#endif /* ZTIMER_PERIODIC_H */
/** @} */

View File

@ -5,7 +5,7 @@ MODULE := ztimer_core
BASE_MODULE := ztimer BASE_MODULE := ztimer
# ztimer_core files # ztimer_core files
SRC := core.c util.c SRC := core.c util.c periodic.c
# enable submodules # enable submodules
SUBMODULES := 1 SUBMODULES := 1

77
sys/ztimer/periodic.c Normal file
View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 2020 Inria
* 2020 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_ztimer
* @{
*
* @file
* @brief ztimer periodic timer implementation
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include "ztimer.h"
#include "ztimer/periodic.h"
static void _ztimer_periodic_reset(ztimer_periodic_t *timer, ztimer_now_t now)
{
ztimer_now_t target = timer->last + timer->interval;
ztimer_now_t offset = target - now;
if (offset > timer->interval) {
offset = 0;
}
timer->last = target;
ztimer_set(timer->clock, &timer->timer, offset);
}
static void _ztimer_periodic_callback(void *arg)
{
ztimer_periodic_t *timer = arg;
ztimer_now_t now = ztimer_now(timer->clock);
if (timer->callback(timer->arg) == ZTIMER_PERIODIC_KEEP_GOING) {
_ztimer_periodic_reset(timer, now);
}
}
void ztimer_periodic_init(ztimer_clock_t *clock, ztimer_periodic_t *timer,
int (*callback)(
void *), void *arg, uint32_t interval)
{
*timer =
(ztimer_periodic_t){ .clock = clock, .interval = interval,
.callback = callback, .arg = arg,
.timer = {
.callback = _ztimer_periodic_callback,
.arg = timer
} };
}
void ztimer_periodic_start(ztimer_periodic_t *timer)
{
uint32_t now = ztimer_now(timer->clock);
timer->last = now;
_ztimer_periodic_reset(timer, now);
}
void ztimer_periodic_stop(ztimer_periodic_t *timer)
{
ztimer_remove(timer->clock, &timer->timer);
}

View File

@ -70,6 +70,7 @@ void ztimer_periodic_wakeup(ztimer_clock_t *clock, uint32_t *last_wakeup,
uint32_t now = ztimer_now(clock); uint32_t now = ztimer_now(clock);
uint32_t target = *last_wakeup + period; uint32_t target = *last_wakeup + period;
uint32_t offset = target - now; uint32_t offset = target - now;
irq_restore(state); irq_restore(state);
if (offset <= period) { if (offset <= period) {

View File

@ -0,0 +1,7 @@
DEVELHELP ?= 0
include ../Makefile.tests_common
USEMODULE += fmt
USEMODULE += ztimer_usec ztimer_msec
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,3 @@
# Introduction
This test application does some basic functionality testing of ztimer_periodic.

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 2020 Freie Universität Berlin
* 2020 Inria
*
* 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 test
* @{
*
* @file
* @brief ztimer periodic test application
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "ztimer.h"
#include "ztimer/periodic.h"
#include "fmt.h"
#include "mutex.h"
static mutex_t _mutex = MUTEX_INIT_LOCKED;
#define MAX_OFFSET 2
#define N 5
#define INTERVAL 100LU
static uint32_t _times[N];
static int callback(void *arg)
{
(void)arg;
static int count = 0;
_times[count] = ztimer_now(ZTIMER_MSEC);
count += 1;
/* enable this to test underflow behavior */
#if 0
if (count == 2) {
ztimer_spin(ZTIMER_MSEC, INTERVAL*2);
}
#endif
if (count == N) {
mutex_unlock(&_mutex);
}
return (count == N);
}
int main(void)
{
ztimer_periodic_t t;
ztimer_periodic_init(ZTIMER_MSEC, &t, callback, NULL, INTERVAL);
uint32_t last = ztimer_now(ZTIMER_MSEC);
ztimer_periodic_start(&t);
/* wait for periodic to trigger N times */
mutex_lock(&_mutex);
int failed = 0;
for (unsigned i = 0; i < N; i++) {
uint32_t offset = labs((int32_t)(_times[i] - INTERVAL - last));
printf("i: %u time: %" PRIu32 " offset: %" PRIu32 "\n",
i, _times[i], offset);
if (offset > MAX_OFFSET) {
failed = 1;
}
last = _times[i];
}
if (!failed) {
print_str("Test successful.\n");
}
else {
print_str("Test failed!\n");
}
}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright (C) 2019 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.
import sys
from testrunner import run
def testfunc(child):
child.expect_exact("Test successful.")
if __name__ == "__main__":
sys.exit(run(testfunc))