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:
parent
a0d41e32e4
commit
5803fb78b8
@ -423,6 +423,7 @@
|
||||
# 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_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_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 */
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <arch/board/board.h>
|
||||
|
||||
#include "up_arch.h"
|
||||
@ -74,24 +75,6 @@
|
||||
* 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 */
|
||||
|
||||
#ifndef CONFIG_DEBUG
|
||||
@ -146,17 +129,19 @@ struct sam_chan_s
|
||||
{
|
||||
struct sam_tc_s *tc; /* Parent timer/counter */
|
||||
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) */
|
||||
bool inuse; /* True: channel is in use */
|
||||
};
|
||||
|
||||
/* This structure describes on timer/counter */
|
||||
/* This structure describes one timer/counter */
|
||||
|
||||
struct sam_tc_s
|
||||
{
|
||||
sem_t exclsem; /* Assures mutually exclusive access to TC */
|
||||
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) */
|
||||
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,
|
||||
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 ***********************************************************/
|
||||
|
||||
#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);
|
||||
|
||||
/****************************************************************************
|
||||
@ -437,28 +440,17 @@ static struct sam_tc_s g_tc678;
|
||||
|
||||
/* 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 */
|
||||
8, /* TIMER_CLOCK2 -> div8 */
|
||||
32, /* TIMER_CLOCK3 -> div32 */
|
||||
128, /* 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 */
|
||||
1, /* TIMER_CLOCK1 -> div2 */
|
||||
3, /* TIMER_CLOCK2 -> div8 */
|
||||
5, /* TIMER_CLOCK3 -> div32 */
|
||||
7 /* TIMER_CLOCK4 -> div128 */
|
||||
};
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
****************************************************************************/
|
||||
/****************************************************************************
|
||||
* 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
|
||||
*
|
||||
@ -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)
|
||||
{
|
||||
FAR struct sam_tc_s *tc;
|
||||
FAR const struct sam_tcconfig_s *tcconfig;
|
||||
FAR struct sam_chan_s *chan;
|
||||
FAR const struct sam_chconfig_s *chconfig;
|
||||
struct sam_tc_s *tc;
|
||||
const struct sam_tcconfig_s *tcconfig;
|
||||
struct sam_chan_s *chan;
|
||||
const struct sam_chconfig_s *chconfig;
|
||||
irqstate_t flags;
|
||||
xcpt_t handler;
|
||||
uint32_t regval;
|
||||
uint8_t ch;
|
||||
int i;
|
||||
@ -742,6 +943,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
||||
{
|
||||
tc = &g_tc012;
|
||||
tcconfig = &g_tc012config;
|
||||
handler = sam_tc012_interrupt;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@ -750,6 +952,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
||||
{
|
||||
tc = &g_tc345;
|
||||
tcconfig = &g_tc345config;
|
||||
handler = sam_tc345_interrupt;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@ -758,6 +961,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
||||
{
|
||||
tc = &g_tc678;
|
||||
tcconfig = &g_tc678config;
|
||||
handler = sam_tc678_interrupt;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@ -820,17 +1024,34 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
||||
|
||||
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 */
|
||||
|
||||
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);
|
||||
|
||||
/* Enable clocking to the timer counter */
|
||||
|
||||
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 */
|
||||
|
||||
tc->initialized = true;
|
||||
@ -980,6 +1201,60 @@ void sam_tc_start(TC_HANDLE handle)
|
||||
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
|
||||
*
|
||||
@ -1009,9 +1284,9 @@ void sam_tc_stop(TC_HANDLE handle)
|
||||
*
|
||||
* Description:
|
||||
* 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
|
||||
* by sam_tc_divider).
|
||||
* by sam_tc_divisor).
|
||||
*
|
||||
* Input Parameters:
|
||||
* 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)
|
||||
{
|
||||
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
|
||||
uint32_t frequency;
|
||||
uint32_t regval;
|
||||
|
||||
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",
|
||||
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_regdump(chan, "Set register");
|
||||
@ -1040,8 +1317,9 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
||||
* Name: sam_tc_frequency
|
||||
*
|
||||
* Description:
|
||||
* Return the timer input frequency, that is, the MCK frequency divided
|
||||
* down so that the timer/counter is driven within its maximum frequency.
|
||||
* Return the timer input frequency (Ftc), that is, the MCK frequency
|
||||
* divided down so that the timer/counter is driven within its maximum
|
||||
* frequency.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
@ -1053,7 +1331,13 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
||||
|
||||
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)
|
||||
*
|
||||
* where:
|
||||
* freq - the desitred frequency
|
||||
* freq - the desired frequency
|
||||
* Ftc - The timer/counter input frequency
|
||||
* 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)
|
||||
{
|
||||
uint32_t ftc = sam_tc_frequency();
|
||||
int ndx = 0;
|
||||
|
||||
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 */
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -1114,11 +1406,12 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
|
||||
|
||||
if (div)
|
||||
{
|
||||
tcvdbg("return div=%d\n", g_divider[ndx]);
|
||||
*div = g_divider[ndx];
|
||||
uint32_t value = sam_tc_freqdiv(ftc, ndx);
|
||||
tcvdbg("return div=%lu\n", (unsigned long)value);
|
||||
*div = value;
|
||||
}
|
||||
|
||||
/* REturn the TCCLKS selection */
|
||||
/* Return the TCCLKS selection */
|
||||
|
||||
if (tcclks)
|
||||
{
|
||||
|
@ -55,12 +55,15 @@
|
||||
|
||||
/* 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_CHAN2 2
|
||||
#define TC_CHAN3 3
|
||||
#define TC_CHAN3 3 /* TC1 */
|
||||
#define TC_CHAN4 4
|
||||
#define TC_CHAN5 5
|
||||
#define TC_CHAN6 6 /* TC2 */
|
||||
#define TC_CHAN7 7
|
||||
#define TC_CHAN8 8
|
||||
|
||||
/* Register identifier used with sam_tc_setregister */
|
||||
|
||||
@ -71,9 +74,21 @@
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
/* An opaque handle used to represent a timer channel */
|
||||
|
||||
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
|
||||
****************************************************************************/
|
||||
@ -144,6 +159,33 @@ void sam_tc_free(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
|
||||
*
|
||||
@ -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
|
||||
* setting in the register will be the TC input frequency divided by
|
||||
* the provided divider (which should derive from the divider returned
|
||||
* by sam_tc_divider).
|
||||
* by sam_tc_divisor).
|
||||
*
|
||||
*
|
||||
* Input Parameters:
|
||||
@ -206,7 +248,7 @@ uint32_t sam_tc_frequency(void);
|
||||
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
|
||||
*
|
||||
* where:
|
||||
* freq - the desitred frequency
|
||||
* freq - the desired frequency
|
||||
* Ftc - The timer/counter input frequency
|
||||
* div - With DIV being the highest possible value.
|
||||
*
|
||||
|
@ -197,7 +197,8 @@ struct watchdog_lowerhalf_s
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
@ -235,8 +236,8 @@ extern "C" {
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
EXTERN FAR void *watchdog_register(FAR const char *path,
|
||||
FAR struct watchdog_lowerhalf_s *lower);
|
||||
FAR void *watchdog_register(FAR const char *path,
|
||||
FAR struct watchdog_lowerhalf_s *lower);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: watchdog_unregister
|
||||
@ -253,7 +254,7 @@ EXTERN FAR void *watchdog_register(FAR const char *path,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
EXTERN void watchdog_unregister(FAR void *handle);
|
||||
void watchdog_unregister(FAR void *handle);
|
||||
|
||||
/****************************************************************************
|
||||
* Platform-Independent "Lower-Half" Watchdog Driver Interfaces
|
||||
@ -282,7 +283,7 @@ EXTERN void watchdog_unregister(FAR void *handle);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
EXTERN int up_wdginitialize(void);
|
||||
int up_wdginitialize(void);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
|
Loading…
Reference in New Issue
Block a user