sched/timer: Fix timer accuracy problems

This commit fixed timer accuracy problems when repetitive timer is set.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen 2024-06-18 20:25:50 +08:00 committed by Xiang Xiao
parent 756faf3cc9
commit a483c884ce
4 changed files with 43 additions and 32 deletions

View File

@ -58,7 +58,9 @@ struct posix_timer_s
uint8_t pt_flags; /* See PT_FLAGS_* definitions */
uint8_t pt_crefs; /* Reference count */
pid_t pt_owner; /* Creator of timer */
int pt_delay; /* If non-zero, used to reset repetitive timers */
int pt_overrun; /* Overrun time */
sclock_t pt_delay; /* If non-zero, used to reset repetitive timers */
clock_t pt_expected; /* Expected absolute time */
struct wdog_s pt_wdog; /* The watchdog that provides the timing */
struct sigevent pt_event; /* Notification information */
struct sigwork_s pt_work;

View File

@ -183,6 +183,7 @@ int timer_create(clockid_t clockid, FAR struct sigevent *evp,
ret->pt_crefs = 1;
ret->pt_owner = nxsched_getpid();
ret->pt_delay = 0;
ret->pt_expected = 0;
/* Was a struct sigevent provided? */

View File

@ -77,9 +77,18 @@
int timer_getoverrun(timer_t timerid)
{
UNUSED(timerid);
set_errno(EINVAL);
return ERROR;
FAR struct posix_timer_s *timer = timer_gethandle(timerid);
int ret;
if (!timer)
{
set_errno(EINVAL);
return ERROR;
}
ret = timer->pt_overrun;
return ret > DELAYTIMER_MAX ? DELAYTIMER_MAX : ret;
}
#endif /* CONFIG_DISABLE_POSIX_TIMERS */

View File

@ -96,11 +96,32 @@ static inline void timer_signotify(FAR struct posix_timer_s *timer)
static inline void timer_restart(FAR struct posix_timer_s *timer,
wdparm_t itimer)
{
clock_t ticks;
sclock_t delay;
/* If this is a repetitive timer, then restart the watchdog */
if (timer->pt_delay)
{
wd_start(&timer->pt_wdog, timer->pt_delay, timer_timeout, itimer);
/* Check whether next expected time is reached */
ticks = clock_systime_ticks();
timer->pt_overrun = 0;
for (; ; )
{
timer->pt_expected += timer->pt_delay;
delay = timer->pt_expected - ticks;
if (delay > 0)
{
break;
}
timer->pt_overrun++;
}
wd_start_absolute(&timer->pt_wdog, timer->pt_expected,
timer_timeout, itimer);
}
}
@ -224,7 +245,6 @@ int timer_settime(timer_t timerid, int flags,
FAR struct itimerspec *ovalue)
{
FAR struct posix_timer_s *timer = timer_gethandle(timerid);
irqstate_t intflags;
sclock_t delay;
int ret = OK;
@ -272,29 +292,20 @@ int timer_settime(timer_t timerid, int flags,
if (value->it_interval.tv_sec > 0 || value->it_interval.tv_nsec > 0)
{
delay = clock_time2ticks(&value->it_interval);
/* REVISIT: Should pt_delay be sclock_t? */
timer->pt_delay = (int)delay;
timer->pt_delay = delay;
}
else
{
timer->pt_delay = 0;
}
/* We need to disable timer interrupts through the following section so
* that the system timer is stable.
*/
intflags = enter_critical_section();
/* Check if abstime is selected */
if ((flags & TIMER_ABSTIME) != 0)
{
/* Calculate a delay corresponding to the absolute time in 'value' */
clock_abstime2ticks(timer->pt_clock, &value->it_value, &delay);
timer->pt_expected = clock_time2ticks(&value->it_value);
}
else
{
@ -304,25 +315,13 @@ int timer_settime(timer_t timerid, int flags,
*/
delay = clock_time2ticks(&value->it_value);
}
/* If the specified time has already passed, the function shall succeed
* and the expiration notification shall be made.
*/
if (delay < 0)
{
delay = 0;
timer->pt_expected = clock_systime_ticks() + delay;
}
/* Then start the watchdog */
if (delay >= 0)
{
ret = wd_start(&timer->pt_wdog, delay, timer_timeout, (wdparm_t)timer);
}
leave_critical_section(intflags);
ret = wd_start_absolute(&timer->pt_wdog, timer->pt_expected,
timer_timeout, (wdparm_t)timer);
if (ret < 0)
{