Stefan Kolb's change to the SAMV7 Oneshot Timer (commit d44ecbcfbb
) should also be applied to the SAM3/4 oneshot time since the drivers are identical. Here are the commit commits from Stefan's original change:
"This is a fix to a problem in the handling of the oneshot timer. Due to a wrong assumption concerning the behavior directly after the start of the timer/counter the function sam_oneshot_cancel(…) calculates the wrong remaining time. The code assumes that the counter register is zero directly after the start of the timer, but this is not true. To start the time/counter a software trigger is invoked, this trigger starts the timer/count and sets the counter register to zero, but the reset of the counter register is not performed instantly. According to the datasheet: “The counter can be reset by a trigger. In this case, the counter value passes to zero on the next valid edge of the selected clock.” Thus the counter is set to zero between 0 and USEC_PER_TICK microseconds after the clock was started. "In my fix I use the freerun count value to determine if at least one tick passed since the start of the timer and thus if the value of the oneshot counter is correct. I also tried to use the function up_timer_gettime(…) to achieve this but, at least if compiled with no optimization the problem vanishes without using the value of the function, the function call takes too long. "Another problem treated in the fix is that if the oneshot timer/counter is canceled, we only know the remaining time with a precision of USEC_PER_TICK microseconds. This means the calculated remaining time is between 0 and USEC_PER_TICK microseconds too long. To fix this I subtract one tick if the calculated remaining time is greater than one tick and otherwise set the remaining time to zero. By doing so the measured times are much more precise as without it."
This commit is contained in:
parent
9071a22c28
commit
fa10927dcc
@ -1060,6 +1060,7 @@ config SAM34_TC5_TIOB
|
||||
|
||||
config SAM34_ONESHOT
|
||||
bool "TC one-shot wrapper"
|
||||
depends on SAM34_FREERUN
|
||||
default n if !SCHED_TICKLESS
|
||||
default y if SCHED_TICKLESS
|
||||
---help---
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include <nuttx/clock.h>
|
||||
|
||||
#include "sam4cm_oneshot.h"
|
||||
#include "sam4cm_freerun.h"
|
||||
|
||||
#ifdef CONFIG_SAM34_ONESHOT
|
||||
|
||||
@ -106,10 +107,11 @@ static void sam_oneshot_handler(TC_HANDLE tch, void *arg, uint32_t sr)
|
||||
|
||||
/* Forward the event, clearing out any vestiges */
|
||||
|
||||
oneshot_handler = (oneshot_handler_t)oneshot->handler;
|
||||
oneshot->handler = NULL;
|
||||
oneshot_arg = (void *)oneshot->arg;
|
||||
oneshot->arg = NULL;
|
||||
oneshot_handler = (oneshot_handler_t)oneshot->handler;
|
||||
oneshot->handler = NULL;
|
||||
oneshot_arg = (void *)oneshot->arg;
|
||||
oneshot->arg = NULL;
|
||||
oneshot->start_count = 0;
|
||||
|
||||
oneshot_handler(oneshot_arg);
|
||||
}
|
||||
@ -206,10 +208,11 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan,
|
||||
* success.
|
||||
*/
|
||||
|
||||
oneshot->chan = chan;
|
||||
oneshot->running = false;
|
||||
oneshot->handler = NULL;
|
||||
oneshot->arg = NULL;
|
||||
oneshot->chan = chan;
|
||||
oneshot->running = false;
|
||||
oneshot->handler = NULL;
|
||||
oneshot->arg = NULL;
|
||||
oneshot->start_count = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -248,8 +251,8 @@ int sam_oneshot_max_delay(struct sam_oneshot_s *oneshot, uint64_t *usec)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
void *arg, const struct timespec *ts)
|
||||
int sam_oneshot_start(struct sam_oneshot_s *oneshot, struct sam_freerun_s *freerun,
|
||||
oneshot_handler_t handler, void *arg, const struct timespec *ts)
|
||||
{
|
||||
uint64_t usec;
|
||||
uint64_t regval;
|
||||
@ -267,7 +270,7 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
/* Yes.. then cancel it */
|
||||
|
||||
tcvdbg("Already running... cancelling\n");
|
||||
(void)sam_oneshot_cancel(oneshot, NULL);
|
||||
(void)sam_oneshot_cancel(oneshot, freerun, NULL);
|
||||
}
|
||||
|
||||
/* Save the new handler and its argument */
|
||||
@ -306,6 +309,26 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
|
||||
sam_tc_start(oneshot->tch);
|
||||
|
||||
/* The function sam_tc_start() starts the timer/counter by setting the
|
||||
* bits TC_CCR_CLKEN and TC_CCR_SWTRG in the channel control register.
|
||||
* The first one enables the timer/counter the latter performs an
|
||||
* software trigger, which starts the clock and sets the counter
|
||||
* register to zero. This reset is performed with the next valid edge
|
||||
* of the selected clock. Thus it can take up USEC_PER_TICK microseconds
|
||||
* until the counter register becomes zero.
|
||||
*
|
||||
* If the timer is canceled within this period the counter register holds
|
||||
* the counter value for the last timer/counter run. To circumvent this
|
||||
* the counter value of the freerun timer/counter is stored at each start
|
||||
* of the oneshot timer/counter.
|
||||
*
|
||||
* The function up_timer_gettime() could also be used for this but it takes
|
||||
* too long. If up_timer_gettime() is called within this function the problem
|
||||
* vanishes at least if compiled with no optimisation.
|
||||
*/
|
||||
|
||||
oneshot->start_count = sam_tc_getcounter(freerun->tch);
|
||||
|
||||
/* Enable interrupts. We should get the callback when the interrupt
|
||||
* occurs.
|
||||
*/
|
||||
@ -340,7 +363,8 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts)
|
||||
int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct sam_freerun_s *freerun,
|
||||
struct timespec *ts)
|
||||
{
|
||||
irqstate_t flags;
|
||||
uint64_t usec;
|
||||
@ -381,6 +405,17 @@ int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts)
|
||||
count = sam_tc_getcounter(oneshot->tch);
|
||||
rc = sam_tc_getregister(oneshot->tch, TC_REGC);
|
||||
|
||||
/* In the case the timer/counter was canceled very short after its start,
|
||||
* the counter register can hold the wrong value (the value of the last
|
||||
* run). To prevent this the counter value is set to zero if not at
|
||||
* least on tick passed since the start of the timer/counter.
|
||||
*/
|
||||
|
||||
if (count > 0 && sam_tc_getcounter(freerun->tch) == oneshot->start_count)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/* Now we can disable the interrupt and stop the timer. */
|
||||
|
||||
sam_tc_attach(oneshot->tch, NULL, NULL, 0);
|
||||
@ -429,6 +464,14 @@ int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts)
|
||||
usec = (((uint64_t)(rc - count)) * USEC_PER_SEC) /
|
||||
sam_tc_divfreq(oneshot->tch);
|
||||
|
||||
/* Each time the timer/counter is canceled the time calculated from
|
||||
* the two registers (counter and REGC) is accurate up to an error
|
||||
* between 0 and USEC_PER_TICK microseconds. To correct this error
|
||||
* one tick which means USEC_PER_TICK microseconds are subtracted.
|
||||
*/
|
||||
|
||||
usec = usec > USEC_PER_TICK ? usec - USEC_PER_TICK : 0;
|
||||
|
||||
/* Return the time remaining in the correct form */
|
||||
|
||||
sec = usec / USEC_PER_SEC;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "sam4cm_tc.h"
|
||||
#include "sam4cm_freerun.h"
|
||||
|
||||
#ifdef CONFIG_SAM34_ONESHOT
|
||||
|
||||
@ -82,6 +83,11 @@ struct sam_oneshot_s
|
||||
volatile oneshot_handler_t handler; /* Oneshot expiration callback */
|
||||
volatile void *arg; /* The argument that will accompany
|
||||
* the callback */
|
||||
volatile uint32_t start_count; /* Stores the value of the freerun counter,
|
||||
* at each start of the onshot timer. Is neccesary
|
||||
* to find out if the onshot counter was updated
|
||||
* correctly at the time of the call to
|
||||
* sam_oneshot_cancel or not. */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
@ -136,6 +142,9 @@ int sam_oneshot_max_delay(struct sam_oneshot_s *oneshot, uint64_t *usec);
|
||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_oneshot_initialize();
|
||||
* freerun Caller allocated instance of the freerun state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_freerun_initialize();
|
||||
* handler The function to call when when the oneshot timer expires.
|
||||
* arg An opaque argument that will accompany the callback.
|
||||
* ts Provides the duration of the one shot timer.
|
||||
@ -146,8 +155,8 @@ int sam_oneshot_max_delay(struct sam_oneshot_s *oneshot, uint64_t *usec);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
void *arg, const struct timespec *ts);
|
||||
int sam_oneshot_start(struct sam_oneshot_s *oneshot, struct sam_freerun_s *freerun,
|
||||
oneshot_handler_t handler, void *arg, const struct timespec *ts);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sam_oneshot_cancel
|
||||
@ -162,6 +171,9 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_oneshot_initialize();
|
||||
* freerun Caller allocated instance of the freerun state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_freerun_initialize();
|
||||
* ts The location in which to return the time remaining on the
|
||||
* oneshot timer. A time of zero is returned if the timer is
|
||||
* not running.
|
||||
@ -173,7 +185,8 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts);
|
||||
int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct sam_freerun_s *freerun,
|
||||
struct timespec *ts);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
|
@ -365,8 +365,8 @@ int up_timer_gettime(FAR struct timespec *ts)
|
||||
|
||||
int up_timer_cancel(FAR struct timespec *ts)
|
||||
{
|
||||
return ONESHOT_INITIALIZED(&g_tickless.oneshot) ?
|
||||
sam_oneshot_cancel(&g_tickless.oneshot, ts) :
|
||||
return ONESHOT_INITIALIZED(&g_tickless.oneshot) && FREERUN_INITIALIZED(&g_tickless.freerun) ?
|
||||
sam_oneshot_cancel(&g_tickless.oneshot, &g_tickless.freerun, ts) :
|
||||
-EAGAIN;
|
||||
}
|
||||
|
||||
@ -398,7 +398,7 @@ int up_timer_cancel(FAR struct timespec *ts)
|
||||
int up_timer_start(FAR const struct timespec *ts)
|
||||
{
|
||||
return ONESHOT_INITIALIZED(&g_tickless.oneshot) ?
|
||||
sam_oneshot_start(&g_tickless.oneshot, sam_oneshot_handler, NULL, ts) :
|
||||
sam_oneshot_start(&g_tickless.oneshot, &g_tickless.freerun, sam_oneshot_handler, NULL, ts) :
|
||||
-EAGAIN;
|
||||
}
|
||||
#endif /* CONFIG_SCHED_TICKLESS */
|
||||
|
Loading…
Reference in New Issue
Block a user