timers/oneshot: Add new start_absolute interface.

Current timer hardware supports both absolute and relative count timers. Without an absolute count timer interface, setting an alarm timer might incur additional overhead in obtaining the current time, resulting in performance degradation and inaccurate timing.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen 2025-07-30 19:49:53 +08:00 committed by Xiang Xiao
parent 6dcb8270e3
commit 9569129862

View file

@ -35,6 +35,7 @@
#include <stdint.h>
#include <time.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/timers/clkcnt.h>
#include <nuttx/fs/ioctl.h>
@ -122,6 +123,9 @@
#define ONESHOT_START(l,t) oneshot_start(l,t)
#define ONESHOT_TICK_START(l,t) oneshot_tick_start(l,t)
#define ONESHOT_ABSOLUTE(l,t) oneshot_start_absolute(l,t)
#define ONESHOT_TICK_ABSOLUTE(l,t) oneshot_tick_absolute(l,t)
/****************************************************************************
* Name: ONESHOT_CANCEL
*
@ -200,6 +204,8 @@ struct oneshot_operations_s
CODE clkcnt_t (*current)(FAR struct oneshot_lowerhalf_s *lower);
CODE void (*start)(FAR struct oneshot_lowerhalf_s *lower,
clkcnt_t delay);
CODE void (*start_absolute)(FAR struct oneshot_lowerhalf_s *lower,
clkcnt_t cnt);
CODE void (*cancel)(FAR struct oneshot_lowerhalf_s *lower);
CODE clkcnt_t (*max_delay)(FAR struct oneshot_lowerhalf_s *lower);
#else
@ -387,6 +393,60 @@ int oneshot_start(FAR struct oneshot_lowerhalf_s *lower,
return ret;
}
/****************************************************************************
* Name: oneshot_start_absolute
*
* Description:
* Set the absolute time to trigger the clockevent.
*
* Input Parameters:
* ops - The oneshot interface.
* expected - The expected time count.
*
* Returned Value:
* None.
*
****************************************************************************/
static inline_function
int oneshot_start_absolute(FAR struct oneshot_lowerhalf_s *lower,
FAR const struct timespec *ts)
{
int ret = OK;
#ifdef CONFIG_ONESHOT_COUNT
uint32_t freq = lower->frequency;
clkcnt_t expected = ts->tv_sec * freq +
clkcnt_delta_time2cnt(ts->tv_nsec, freq, NSEC_PER_SEC);
if (lower->ops->start_absolute)
{
lower->ops->start_absolute(lower, expected);
}
else
{
/* IRQ should be disable or the timer will be fired too late. */
irqstate_t flags = up_irq_save();
clkcnt_t delay = expected - lower->ops->current(lower);
lower->ops->start(lower, delay);
up_irq_restore(flags);
}
#else
struct timespec curr;
/* Some timer drivers may not have current() function.
* Since only arch_alarm uses the function, it should be OK.
*/
DEBUGASSERT(lower->ops->current);
ret = lower->ops->current(lower, &curr);
clock_timespec_subtract(ts, &curr, &curr);
ret = lower->ops->start(lower, &curr);
#endif
return ret;
}
/* Tick-based compatiable layer for oneshot */
static inline_function
@ -479,6 +539,34 @@ int oneshot_tick_current(FAR struct oneshot_lowerhalf_s *lower,
return ret;
}
static inline_function
int oneshot_tick_absolute(FAR struct oneshot_lowerhalf_s *lower,
clock_t tick)
{
int ret = OK;
#ifdef CONFIG_ONESHOT_COUNT
clkcnt_t expected = clkcnt_tick2cnt(tick, lower->frequency);
if (lower->ops->start_absolute)
{
lower->ops->start_absolute(lower, expected);
}
else
{
/* IRQ should be disable or the timer will be fired too late. */
irqstate_t flags = up_irq_save();
clkcnt_t delay = expected - lower->ops->current(lower);
lower->ops->start(lower, delay);
up_irq_restore(flags);
}
#else
struct timespec ts;
clock_ticks2time(&ts, tick);
ret = oneshot_start_absolute(lower, &ts);
#endif
return ret;
}
/****************************************************************************
* Name: oneshot_tick_cancel
*