diff --git a/arch/arm/src/sam34/Kconfig b/arch/arm/src/sam34/Kconfig index ed14f21ade6..b704da5674f 100644 --- a/arch/arm/src/sam34/Kconfig +++ b/arch/arm/src/sam34/Kconfig @@ -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--- diff --git a/arch/arm/src/sam34/sam4cm_oneshot.c b/arch/arm/src/sam34/sam4cm_oneshot.c index a793350d66c..eeda41bc8ee 100644 --- a/arch/arm/src/sam34/sam4cm_oneshot.c +++ b/arch/arm/src/sam34/sam4cm_oneshot.c @@ -58,6 +58,7 @@ #include #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; diff --git a/arch/arm/src/sam34/sam4cm_oneshot.h b/arch/arm/src/sam34/sam4cm_oneshot.h index 3c3266b5db6..d7dc7a18ee0 100644 --- a/arch/arm/src/sam34/sam4cm_oneshot.h +++ b/arch/arm/src/sam34/sam4cm_oneshot.h @@ -46,6 +46,7 @@ #include #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 diff --git a/arch/arm/src/sam34/sam4cm_tickless.c b/arch/arm/src/sam34/sam4cm_tickless.c index 04952413314..7d13e8756ef 100644 --- a/arch/arm/src/sam34/sam4cm_tickless.c +++ b/arch/arm/src/sam34/sam4cm_tickless.c @@ -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 */