SAMA5 T/C: Can now handle non-constant BOARD_MCK_FREQUENCY. Also now supports methods to attach user interrupt handlers

This commit is contained in:
Gregory Nutt 2014-08-09 10:30:45 -06:00
parent 5d143578b0
commit c7662e3f92
3 changed files with 401 additions and 65 deletions

View File

@ -423,6 +423,7 @@
# define SAMA5_HAVE_PMC_PCR_DIV 1 /* Supports conditional compilation */ # define SAMA5_HAVE_PMC_PCR_DIV 1 /* Supports conditional compilation */
# define PMC_PCR_DIV_SHIFT (16) /* Bits 16-17: Divisor Value */ # define PMC_PCR_DIV_SHIFT (16) /* Bits 16-17: Divisor Value */
# define PMC_PCR_DIV_MASK (3 << PMC_PCR_DIV_SHIFT) # define PMC_PCR_DIV_MASK (3 << PMC_PCR_DIV_SHIFT)
# define PMC_PCR_DIV(n) ((uint32_t)(n) << PMC_PCR_DIV_SHIFT)
# define PMC_PCR_DIV1 (0 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */ # define PMC_PCR_DIV1 (0 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */
# define PMC_PCR_DIV2 (1 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */ # define PMC_PCR_DIV2 (1 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */
# define PMC_PCR_DIV4 (2 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */ # define PMC_PCR_DIV4 (2 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */

View File

@ -58,6 +58,7 @@
#include <errno.h> #include <errno.h>
#include <debug.h> #include <debug.h>
#include <nuttx/arch.h>
#include <arch/board/board.h> #include <arch/board/board.h>
#include "up_arch.h" #include "up_arch.h"
@ -74,24 +75,6 @@
* Pre-processor Definitions * Pre-processor Definitions
****************************************************************************/ ****************************************************************************/
/* Clocking */
#if BOARD_MCK_FREQUENCY <= SAM_TC_MAXPERCLK
# define TC_FREQUENCY BOARD_MCK_FREQUENCY
# define TC_PCR_DIV PMC_PCR_DIV1
#elif (BOARD_MCK_FREQUENCY >> 1) <= SAM_TC_MAXPERCLK
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 1)
# define TC_PCR_DIV PMC_PCR_DIV2
#elif (BOARD_MCK_FREQUENCY >> 2) <= SAM_TC_MAXPERCLK
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 2)
# define TC_PCR_DIV PMC_PCR_DIV4
#elif (BOARD_MCK_FREQUENCY >> 3) <= SAM_TC_MAXPERCLK
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 3)
# define TC_PCR_DIV PMC_PCR_DIV8
#else
# error Cannot realize TC input frequency
#endif
/* Timer debug is enabled if any timer client is enabled */ /* Timer debug is enabled if any timer client is enabled */
#ifndef CONFIG_DEBUG #ifndef CONFIG_DEBUG
@ -146,17 +129,19 @@ struct sam_chan_s
{ {
struct sam_tc_s *tc; /* Parent timer/counter */ struct sam_tc_s *tc; /* Parent timer/counter */
uintptr_t base; /* Channel register base address */ uintptr_t base; /* Channel register base address */
tc_handler_t handler; /* Attached interrupt handler */
void *arg; /* Interrupt handler argument */
uint8_t chan; /* Channel number (0, 1, or 2 OR 3, 4, or 5) */ uint8_t chan; /* Channel number (0, 1, or 2 OR 3, 4, or 5) */
bool inuse; /* True: channel is in use */ bool inuse; /* True: channel is in use */
}; };
/* This structure describes on timer/counter */ /* This structure describes one timer/counter */
struct sam_tc_s struct sam_tc_s
{ {
sem_t exclsem; /* Assures mutually exclusive access to TC */ sem_t exclsem; /* Assures mutually exclusive access to TC */
uintptr_t base; /* Register base address */ uintptr_t base; /* Register base address */
uint8_t pid; /* Peripheral ID */ uint8_t pid; /* Peripheral ID/irq number */
uint8_t tc; /* Timer/channel number (0 or 1) */ uint8_t tc; /* Timer/channel number (0 or 1) */
bool initialized; /* True: Timer data has been initialized */ bool initialized; /* True: Timer data has been initialized */
@ -202,8 +187,26 @@ static inline uint32_t sam_chan_getreg(struct sam_chan_s *chan,
static inline void sam_chan_putreg(struct sam_chan_s *chan, static inline void sam_chan_putreg(struct sam_chan_s *chan,
unsigned int offset, uint32_t regval); unsigned int offset, uint32_t regval);
/* Interrupt Handling *******************************************************/
static int sam_tc_interrupt(struct sam_tc_s *tc);
#ifdef CONFIG_SAMA5_TC0
static int sam_tc012_interrupt(int irq, void *context);
#endif
#ifdef CONFIG_SAMA5_TC1
static int sam_tc345_interrupt(int irq, void *context);
#endif
#ifdef CONFIG_SAMA5_TC2
static int sam_tc678_interrupt(int irq, void *context);
#endif
/* Initialization ***********************************************************/ /* Initialization ***********************************************************/
#ifdef SAMA5_HAVE_PMC_PCR_DIV
static int sam_tc_mckdivider(uint32_t mck);
#endif
static int sam_tc_freqdiv(uint32_t ftc, int ndx);
static uint32_t sam_tc_divfreq(uint32_t ftc, int ndx);
static inline struct sam_chan_s *sam_tc_initialize(int channel); static inline struct sam_chan_s *sam_tc_initialize(int channel);
/**************************************************************************** /****************************************************************************
@ -437,28 +440,17 @@ static struct sam_tc_s g_tc678;
/* TC frequency data. This table provides the frequency for each selection of TCCLK */ /* TC frequency data. This table provides the frequency for each selection of TCCLK */
#define TC_NDIVIDERS 5 #define TC_NDIVIDERS 4
#define TC_NDIVOPTIONS 5
/* This is the list of divider values */ /* This is the list of divider values: divider = (1 << value) */
static const uint16_t g_divider[TC_NDIVIDERS] = static const uint8_t g_log2divider[TC_NDIVIDERS] =
{ {
2, /* TIMER_CLOCK1 -> div2 */ 1, /* TIMER_CLOCK1 -> div2 */
8, /* TIMER_CLOCK2 -> div8 */ 3, /* TIMER_CLOCK2 -> div8 */
32, /* TIMER_CLOCK3 -> div32 */ 5, /* TIMER_CLOCK3 -> div32 */
128, /* TIMER_CLOCK4 -> div128 */ 7 /* TIMER_CLOCK4 -> div128 */
TC_FREQUENCY / 32768 /* TIMER_CLOCK5 -> slow clock (not really a divider) */
};
/* This is the list of divided down frequencies */
static const uint32_t g_divfreq[TC_NDIVIDERS] =
{
TC_FREQUENCY / 2, /* TIMER_CLOCK1 -> div2 */
TC_FREQUENCY / 8, /* TIMER_CLOCK2 -> div8 */
TC_FREQUENCY / 32, /* TIMER_CLOCK3 -> div32 */
TC_FREQUENCY / 128, /* TIMER_CLOCK4 -> div128 */
32768 /* TIMER_CLOCK5 -> slow clock */
}; };
/* TC register lookup used by sam_tc_setregister */ /* TC register lookup used by sam_tc_setregister */
@ -699,9 +691,217 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset,
putreg32(regval, regaddr); putreg32(regval, regaddr);
} }
/****************************************************************************
* Interrupt Handling
****************************************************************************/
/****************************************************************************
* Name: sam_tc_interrupt
*
* Description:
* Common timer channel interrupt handling.
*
* Input Parameters:
* tc Timer status instance
*
* Returned Value:
* A pointer to the initialized timer channel structure associated with tc
* and channel. NULL is returned on any failure.
*
* On successful return, the caller holds the tc exclusive access semaphore.
*
****************************************************************************/
static int sam_tc_interrupt(struct sam_tc_s *tc)
{
struct sam_chan_s *chan;
uint32_t sr;
uint32_t imr;
uint32_t pending;
int i;
/* Process interrupts on each channel */
for (i = 0; i < 3; i++)
{
/* Get the handy channel reference */
chan = &tc->channel[i];
/* Get the interrupt status for this channel */
sr = sam_chan_getreg(chan, SAM_TC_SR_OFFSET);
imr = sam_chan_getreg(chan, SAM_TC_IMR_OFFSET);
pending = sr & imr;
/* Are there any pending interrupts for this channel? */
if (pending)
{
/* Yes... if we have pending interrupts then interrupts must be
* enabled and we must have a handler attached.
*/
DEBUGASSERT(chan->handler);
if (chan->handler)
{
/* Execute the callback */
chan->handler(chan, chan->arg, sr);
}
else
{
/* Should never happen */
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
}
}
}
return OK;
}
/****************************************************************************
* Name: sam_tcABC_interrupt
*
* Description:
* Timer block interrupt handlers
*
* Input Parameters:
* chan TC channel structure
* sr The status register value that generated the interrupt
*
* Returned Value:
* A pointer to the initialized timer channel structure associated with tc
* and channel. NULL is returned on any failure.
*
* On successful return, the caller holds the tc exclusive access semaphore.
*
****************************************************************************/
#ifdef CONFIG_SAMA5_TC0
static int sam_tc012_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc012);
}
#endif
#ifdef CONFIG_SAMA5_TC1
static int sam_tc345_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc345);
}
#endif
#ifdef CONFIG_SAMA5_TC2
static int sam_tc678_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc678);
}
#endif
/**************************************************************************** /****************************************************************************
* Initialization * Initialization
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: sam_tc_mckdivider
*
* Description:
* Return the TC clock input divider value. One of n=0..3 corresponding
* to divider values of {1, 2, 4, 8}.
*
* NOTE: The SAMA5D4 has no clock input divider
*
* Input Parameters:
* mck - The MCK frequency to be divider.
*
* Returned Value:
* Log2 of the TC clock divider.
*
****************************************************************************/
#ifdef SAMA5_HAVE_PMC_PCR_DIV
static int sam_tc_mckdivider(uint32_t mck)
{
if (mck <= SAM_TC_MAXPERCLK)
{
return 0;
}
else if ((mck >> 1) <= SAM_TC_MAXPERCLK)
{
return 1;
}
else if ((mck >> 2) <= SAM_TC_MAXPERCLK)
{
return 2;
}
else /* if ((mck >> 3) <= SAM_TC_MAXPERCLK) */
{
DEBUGASSERT((mck >> 3) <= SAM_TC_MAXPERCLK);
return 3;
}
}
#endif
/****************************************************************************
* Name: sam_tc_freqdiv
*
* Description:
* Given the TC input frequency (Ftc) and a divider index, return the value of
* the Ftc divider.
*
* Input Parameters:
* ftc - TC input frequency
* ndx - Divider index
*
* Returned Value:
* The ftc input divider value
*
****************************************************************************/
static int sam_tc_freqdiv(uint32_t ftc, int ndx)
{
/* The final option is to use the SLOW clock */
if (ndx >= TC_NDIVIDERS)
{
return ftc / BOARD_SLOWCLK_FREQUENCY;
}
else
{
return 1 << g_log2divider[ndx];
}
}
/****************************************************************************
* Name: sam_tc_divfreq
*
* Description:
* Given the TC input frequency (Ftc) and a divider index, return the value of
* the divided frequency
*
* Input Parameters:
* ftc - TC input frequency
* ndx - Divider index
*
* Returned Value:
* The divided frequency value
*
****************************************************************************/
static uint32_t sam_tc_divfreq(uint32_t ftc, int ndx)
{
/* The final option is to use the SLOW clock */
if (ndx >= TC_NDIVIDERS)
{
return BOARD_SLOWCLK_FREQUENCY;
}
else
{
return ftc >> g_log2divider[ndx];
}
}
/**************************************************************************** /****************************************************************************
* Name: sam_tc_initialize * Name: sam_tc_initialize
* *
@ -724,11 +924,12 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset,
static inline struct sam_chan_s *sam_tc_initialize(int channel) static inline struct sam_chan_s *sam_tc_initialize(int channel)
{ {
FAR struct sam_tc_s *tc; struct sam_tc_s *tc;
FAR const struct sam_tcconfig_s *tcconfig; const struct sam_tcconfig_s *tcconfig;
FAR struct sam_chan_s *chan; struct sam_chan_s *chan;
FAR const struct sam_chconfig_s *chconfig; const struct sam_chconfig_s *chconfig;
irqstate_t flags; irqstate_t flags;
xcpt_t handler;
uint32_t regval; uint32_t regval;
uint8_t ch; uint8_t ch;
int i; int i;
@ -742,6 +943,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{ {
tc = &g_tc012; tc = &g_tc012;
tcconfig = &g_tc012config; tcconfig = &g_tc012config;
handler = sam_tc012_interrupt;
} }
else else
#endif #endif
@ -750,6 +952,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{ {
tc = &g_tc345; tc = &g_tc345;
tcconfig = &g_tc345config; tcconfig = &g_tc345config;
handler = sam_tc345_interrupt;
} }
else else
#endif #endif
@ -758,6 +961,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{ {
tc = &g_tc678; tc = &g_tc678;
tcconfig = &g_tc678config; tcconfig = &g_tc678config;
handler = sam_tc678_interrupt;
} }
else else
#endif #endif
@ -820,17 +1024,34 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
sam_configpio(chconfig->tiobset); sam_configpio(chconfig->tiobset);
} }
/* Disable and clear all channel interrupts */
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
(void)sam_chan_getreg(chan, SAM_TC_SR_OFFSET);
} }
/* Set the maximum TC peripheral clock frequency */ /* Set the maximum TC peripheral clock frequency */
regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | TC_PCR_DIV | PMC_PCR_EN; regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | PMC_PCR_EN;
#ifdef SAMA5_HAVE_PMC_PCR_DIV
/* Set the MCK divider (if any) */
regval |= PMC_PCR_DIV(sam_tc_mckdivider(BOARD_MCK_FREQUENCY));
#endif
putreg32(regval, SAM_PMC_PCR); putreg32(regval, SAM_PMC_PCR);
/* Enable clocking to the timer counter */ /* Enable clocking to the timer counter */
sam_enableperiph0(tcconfig->pid); sam_enableperiph0(tcconfig->pid);
/* Attach the timer interrupt handler and enable the timer interrupts */
(void)irq_attach(tc->pid, handler);
up_enable_irq(tc->pid);
/* Now the channel is initialized */ /* Now the channel is initialized */
tc->initialized = true; tc->initialized = true;
@ -980,6 +1201,60 @@ void sam_tc_start(TC_HANDLE handle)
sam_regdump(chan, "Started"); sam_regdump(chan, "Started");
} }
/****************************************************************************
* Name: sam_tc_attach
*
* Description:
* Attach or detach an interrupt handler to the timer interrupt. The
* interrupt is detached if the handler argument is NULL.
*
* Input Parameters:
* handle The handle that represents the timer state
* handler The interrupt handler that will be invoked when the interrupt
* condition occurs
* arg An opaque argument that will be provided when the interrupt
* handler callback is executed.
* mask The value of the timer interrupt mask register that defines
* which interrupts should be disabled.
*
* Returned Value:
*
****************************************************************************/
tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
void *arg, uint32_t mask)
{
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
tc_handler_t oldhandler;
irqstate_t flags;
DEBUGASSERT(chan);
/* Remember the old interrupt handler and set the new handler */
flags = irqsave();
oldhandler = handler;
chan->handler = handler;
/* Don't enable interrupt if we are detaching no matter what the caller
* says.
*/
if (!handler)
{
arg = NULL;
mask = 0;
}
/* Now enable interrupt as requested */
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL & ~mask);
sam_chan_putreg(chan, SAM_TC_IER_OFFSET, TC_INT_ALL & mask);
irqrestore(flags);
return oldhandler;
}
/**************************************************************************** /****************************************************************************
* Name: sam_tc_stop * Name: sam_tc_stop
* *
@ -1009,9 +1284,9 @@ void sam_tc_stop(TC_HANDLE handle)
* *
* Description: * Description:
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
* setting in the register will be the TC input frequency divided by * setting in the register will be the TC input frequency (Ftc) divided by
* the provided divider (which should derive from the divider returned * the provided divider (which should derive from the divider returned
* by sam_tc_divider). * by sam_tc_divisor).
* *
* Input Parameters: * Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate() * handle Channel handle previously allocated by sam_tc_allocate()
@ -1024,13 +1299,15 @@ void sam_tc_stop(TC_HANDLE handle)
void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div) void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
{ {
struct sam_chan_s *chan = (struct sam_chan_s *)handle; struct sam_chan_s *chan = (struct sam_chan_s *)handle;
uint32_t frequency;
uint32_t regval; uint32_t regval;
DEBUGASSERT(reg < TC_NREGISTERS); DEBUGASSERT(reg < TC_NREGISTERS);
regval = TC_FREQUENCY / div; frequency = sam_tc_frequency();
regval = frequency / div;
tcvdbg("Channel %d: Set register %d to %d / %d = %d\n", tcvdbg("Channel %d: Set register %d to %d / %d = %d\n",
chan->chan, reg, TC_FREQUENCY, div, (unsigned int)regval); chan->chan, reg, frequency, div, (unsigned int)regval);
sam_chan_putreg(chan, g_regoffset[reg], regval); sam_chan_putreg(chan, g_regoffset[reg], regval);
sam_regdump(chan, "Set register"); sam_regdump(chan, "Set register");
@ -1040,8 +1317,9 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
* Name: sam_tc_frequency * Name: sam_tc_frequency
* *
* Description: * Description:
* Return the timer input frequency, that is, the MCK frequency divided * Return the timer input frequency (Ftc), that is, the MCK frequency
* down so that the timer/counter is driven within its maximum frequency. * divided down so that the timer/counter is driven within its maximum
* frequency.
* *
* Input Parameters: * Input Parameters:
* None * None
@ -1053,7 +1331,13 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
uint32_t sam_tc_frequency(void) uint32_t sam_tc_frequency(void)
{ {
return TC_FREQUENCY; #ifdef SAMA5_HAVE_PMC_PCR_DIV
uint32_t mck = BOARD_MCK_FREQUENCY;
int shift = sam_tc_mckdivider(mck);
return mck >> shift;
#else
return BOARD_MCK_FREQUENCY;
#endif
} }
/**************************************************************************** /****************************************************************************
@ -1066,7 +1350,7 @@ uint32_t sam_tc_frequency(void)
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev) * (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
* *
* where: * where:
* freq - the desitred frequency * freq - the desired frequency
* Ftc - The timer/counter input frequency * Ftc - The timer/counter input frequency
* div - With DIV being the highest possible value. * div - With DIV being the highest possible value.
* *
@ -1083,15 +1367,19 @@ uint32_t sam_tc_frequency(void)
int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks) int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
{ {
uint32_t ftc = sam_tc_frequency();
int ndx = 0; int ndx = 0;
tcvdbg("frequency=%d\n", frequency); tcvdbg("frequency=%d\n", frequency);
/* Satisfy lower bound */ /* Satisfy lower bound. That is, the value of the divider such that:
*
* frequency >= tc_input_frequency / divider.
*/
while (frequency < (g_divfreq[ndx] >> 16)) while (frequency < (sam_tc_divfreq(ftc, ndx) >> 16))
{ {
if (++ndx > TC_NDIVIDERS) if (++ndx > TC_NDIVOPTIONS)
{ {
/* If no divisor can be found, return -ERANGE */ /* If no divisor can be found, return -ERANGE */
@ -1100,11 +1388,15 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
} }
} }
/* Try to maximize DIV while still satisfying upper bound */ /* Try to maximize DIV while still satisfying upper bound. That the
* value of the divider such that:
*
* frequency < tc_input_frequency / divider.
*/
for (; ndx < (TC_NDIVIDERS-1); ndx++) for (; ndx < (TC_NDIVOPTIONS-1); ndx++)
{ {
if (frequency > g_divfreq[ndx + 1]) if (frequency > sam_tc_divfreq(ftc, ndx + 1))
{ {
break; break;
} }
@ -1114,11 +1406,12 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
if (div) if (div)
{ {
tcvdbg("return div=%d\n", g_divider[ndx]); uint32_t value = sam_tc_freqdiv(ftc, ndx);
*div = g_divider[ndx]; tcvdbg("return div=%lu\n", (unsigned long)value);
*div = value;
} }
/* REturn the TCCLKS selection */ /* Return the TCCLKS selection */
if (tcclks) if (tcclks)
{ {

View File

@ -55,12 +55,15 @@
/* The timer/counter and channel arguments to sam_tc_allocate() */ /* The timer/counter and channel arguments to sam_tc_allocate() */
#define TC_CHAN0 0 #define TC_CHAN0 0 /* TC0 */
#define TC_CHAN1 1 #define TC_CHAN1 1
#define TC_CHAN2 2 #define TC_CHAN2 2
#define TC_CHAN3 3 #define TC_CHAN3 3 /* TC1 */
#define TC_CHAN4 4 #define TC_CHAN4 4
#define TC_CHAN5 5 #define TC_CHAN5 5
#define TC_CHAN6 6 /* TC2 */
#define TC_CHAN7 7
#define TC_CHAN8 8
/* Register identifier used with sam_tc_setregister */ /* Register identifier used with sam_tc_setregister */
@ -71,9 +74,21 @@
/**************************************************************************** /****************************************************************************
* Public Types * Public Types
****************************************************************************/ ****************************************************************************/
/* An opaque handle used to represent a timer channel */
typedef void *TC_HANDLE; typedef void *TC_HANDLE;
/* Timer interrupt callback. When a timer interrup expires, the client will
* receive:
*
* handle - The handle that represents the timer state
* arg - An opaque argument provided when the interrupt was registered
* sr - The value of the timer interrupt status register at the time
* that the interrupt occurred.
*/
typedef void (*tc_handler_t)(TC_HANDLE handle, void *arg, uint32_t sr);
/**************************************************************************** /****************************************************************************
* Public Data * Public Data
****************************************************************************/ ****************************************************************************/
@ -144,6 +159,33 @@ void sam_tc_free(TC_HANDLE handle);
void sam_tc_start(TC_HANDLE handle); void sam_tc_start(TC_HANDLE handle);
/****************************************************************************
* Name: sam_tc_attach/sam_tc_detach
*
* Description:
* Attach or detach an interrupt handler to the timer interrupt. The
* interrupt is detached if the handler argument is NULL.
*
* Input Parameters:
* handle The handle that represents the timer state
* handler The interrupt handler that will be invoked when the interrupt
* condition occurs
* arg An opaque argument that will be provided when the interrupt
* handler callback is executed. Ignored if handler is NULL.
* mask The value of the timer interrupt mask register that defines
* which interrupts should be disabled. Ignored if handler is
* NULL.
*
* Returned Value:
* The address of the previous handler, if any.
*
****************************************************************************/
tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
void *arg, uint32_t mask);
#define sam_tc_detach(h) sam_tc_attach(h, NULL, NULL, 0)
/**************************************************************************** /****************************************************************************
* Name: sam_tc_stop * Name: sam_tc_stop
* *
@ -166,7 +208,7 @@ void sam_tc_stop(TC_HANDLE handle);
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
* setting in the register will be the TC input frequency divided by * setting in the register will be the TC input frequency divided by
* the provided divider (which should derive from the divider returned * the provided divider (which should derive from the divider returned
* by sam_tc_divider). * by sam_tc_divisor).
* *
* *
* Input Parameters: * Input Parameters:
@ -206,7 +248,7 @@ uint32_t sam_tc_frequency(void);
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev) * (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
* *
* where: * where:
* freq - the desitred frequency * freq - the desired frequency
* Ftc - The timer/counter input frequency * Ftc - The timer/counter input frequency
* div - With DIV being the highest possible value. * div - With DIV being the highest possible value.
* *