SAMA5: Hook in timer/counter logic so that it can driver periodic ADC sampling

This commit is contained in:
Gregory Nutt 2013-10-24 12:35:42 -06:00
parent 21c59c0aa7
commit aab5b6b2d9
7 changed files with 603 additions and 77 deletions

View File

@ -5871,4 +5871,6 @@
(2013-10-24).
* configs/olimex-stm32-p207 and other files: Support for the Olimex
STM32 P207 board added by Martin Lederhilger (2017-10-24).
* arch/arm/src/sama5/sam_adc.c, sam_tc.c and sam_tc.h: Hook in the
timer/counter logic so that it can driver periodic ADC sampling
(2013-10-24).

View File

@ -2070,6 +2070,7 @@ config SAMA5_ADC_ADTRG
config SAMA5_ADC_TIOATRIG
bool "TC0 ouput A trigger"
depends on SAMA5_TC0
---help---
A-to-D Conversion is initiated the A output from one of
Timer/Counter 0 channels.
@ -2100,12 +2101,20 @@ endchoice # ADTRG edge
if SAMA5_ADC_TIOATRIG
config SAMA5_ADC_TIOAFREQ
int "ADC sampling frequency"
default 1
---help---
This setting provides the rate at which the timer will driver ADC
sampling.
choice
prompt "TC0 channel"
default SAMA5_ADC_TIOA0TRIG
config SAMA5_ADC_TIOA0TRIG
bool "TC0 Channel 0 Output A"
select SAMA5_TC0_TIOA0
---help---
A-to-D conversion is triggered by the TC0 channel 0 output A signal.
This output must be enabled independently in the Timer/Counter
@ -2113,6 +2122,7 @@ config SAMA5_ADC_TIOA0TRIG
config SAMA5_ADC_TIOA1TRIG
bool "TC0 Channel 1 Output A"
select SAMA5_TC0_TIOA1
---help---
A-to-D conversion is triggered by the TC0 channel 1 output A signal.
This output must be enabled independently in the Timer/Counter
@ -2120,6 +2130,7 @@ config SAMA5_ADC_TIOA1TRIG
config SAMA5_ADC_TIOA2TRIG
bool "TC0 Channel 2 Output A"
select SAMA5_TC0_TIOA2
---help---
A-to-D conversion is triggered by the TC0 channel 2 output A signal.
This output must be enabled independently in the Timer/Counter
@ -2161,6 +2172,92 @@ config SAMA5_ADC_REGDEBUG
endmenu # ADC Configuration
if SAMA5_TC0 || SAMA5_TC1
menu "Timer/counter Configuration"
if SAMA5_TC0
config SAMA5_TC0_CLK0
bool "Enable TC0 channel 0 clock input pin"
default n
config SAMA5_TC0_TIOA0
bool "Enable TC0 channel 0 ouput A"
default n
config SAMA5_TC0_TIOB0
bool "Enable TC0 channel 0 ouput B"
default n
config SAMA5_TC0_CLK1
bool "Enable TC0 channel 1 clock input pin"
default n
config SAMA5_TC0_TIOA1
bool "Enable TC0 channel 1 ouput A"
default n
config SAMA5_TC0_TIOB1
bool "Enable TC0 channel 1 ouput B"
default n
config SAMA5_TC0_CLK2
bool "Enable TC0 channel 2 clock input pin"
default n
config SAMA5_TC0_TIOA2
bool "Enable TC0 channel 2 ouput A"
default n
config SAMA5_TC0_TIOB2
bool "Enable TC0 channel 2 ouput B"
default n
endif # SAMA5_TC0
if SAMA5_TC1
config SAMA5_TC1_CLK3
bool "Enable TC1 channel 3 clock input pin"
default n
config SAMA5_TC1_TIOA3
bool "Enable TC1 channel 3 ouput A"
default n
config SAMA5_TC1_TIOB3
bool "Enable TC1 channel 3 ouput B"
default n
config SAMA5_TC1_CLK4
bool "Enable TC1 channel 4 clock input pin"
default n
config SAMA5_TC1_TIOA4
bool "Enable TC1 channel 4 ouput A"
default n
config SAMA5_TC1_TIOB4
bool "Enable TC1 channel 4 ouput B"
default n
config SAMA5_TC1_CLK5
bool "Enable TC1 channel 5 clock input pin"
default n
config SAMA5_TC1_TIOA5
bool "Enable TC1 channel 5 ouput A"
default n
config SAMA5_TC1_TIOB5
bool "Enable TC1 channel 5 ouput B"
default n
endif # SAMA5_TC1
endmenu # Timer/counter Configuration
endif # SAMA5_TC0 || SAMA5_TC1
menu "Touchscreen configuration"
config SAMA5_TSD

View File

@ -46,6 +46,10 @@
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
#define SAM_TC_NCHANNELS 3 /* Number of channels per TC peripheral */
#define SAM_TC_MAXPERCLK 66000000 /* Maximum peripheral input clock frequency */
/* TC Register Offsets **************************************************************/
#define SAM_TC_CHAN_OFFSET(n) ((n) << 6) /* Channel n offset */

View File

@ -71,6 +71,7 @@
#include "chip/sam_pmc.h"
#include "sam_periphclks.h"
#include "sam_dmac.h"
#include "sam_tc.h"
#include "sam_tsd.h"
#include "sam_adc.h"
@ -379,6 +380,9 @@ struct sam_adc_s
#ifdef CONFIG_SAMA5_ADC_DMA
DMA_HANDLE dma; /* Handle for DMA channel */
#endif
#ifdef CONFIG_SAMA5_ADC_TIOATRIG
TC_HANDLE tc; /* Handle for the timer channel */
#endif
/* DMA sample data buffer */
@ -435,6 +439,10 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);
/* Initialization/Configuration */
#ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
int channel, boot tioa);
#endif
static void sam_adc_trigger(struct sam_adc_s *priv);
static void sam_adc_autocalibrate(struct sam_adc_s *priv);
static void sam_adc_offset(struct sam_adc_s *priv);
@ -878,6 +886,12 @@ static void sam_adc_reset(struct adc_dev_s *dev)
dma_stop(priv->dma);
/* Stop an release any timer */
#ifdef CONFIG_SAMA5_ADC_TIOATRIG
sam_adc_freetimer(priv);
#endif
/* Disable all EOC interrupts */
sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_EOCALL);
@ -1067,6 +1081,85 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg)
* Initialization/Configuration
****************************************************************************/
/****************************************************************************
* Name: sam_adc_settimer
*
* Description:
* Configure a timer to trigger the sampling periodically
*
****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
int channel)
{
uint32_t div;
uint32_t tcclks;
/* Configure TC for a 1Hz frequency and trigger on RC compare. */
ret = sam_tc_divisor(frequency, &div, &tcclks);
if (ret < 0)
{
adbg("ERROR: sam_tc_divisor failed: %d\n", ret);
return ret;
}
/* Set the timer/counter waveform mode the the clock input slected by
* sam_tc_divisor()
*/
mode = ((tcclks << TC_CMR_TCCLKS_SHIFT) | /* Use selected TCCLKS value */
TC_CMR_WAVSEL_UPRC | /* UP mode w/ trigger on RC Compare */
TC_CMR_WAVE | /* Wave mode */
TC_CMR_ACPA_CLEAR | /* RA Compare Effect on TIOA: Clear */
TC_CMR_ACPC_SET); /* RC effect on TIOA: Set
/* Now allocate and configure the channel */
priv->tc = sam_tc_allocate(channel, mode);
if (!priv->tc)
{
adbg("ERROR: Failed to allocate channel %d mode %08x\n", channel, mode);
return -EINVAL;
}
/* Set up TC_RA and TC_RC */
sam_tc_setregister(priv->tc, TC_REGA, div / 2);
sam_tc_setregister(priv->tc, TC_REGC, div);
/* And start the timer */
sam_tc_start(priv->tc);
}
#endif
/****************************************************************************
* Name: sam_adc_settimer
*
* Description:
* Configure a timer to trigger the sampling periodically
*
****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
int channel)
{
/* Is a timer allocated? */
if (priv->tc)
{
/* Yes.. stop it and free it */
sam_tc_stop(priv->tc);
sam_tc_free(priv->tc);
priv->tc = NULL;
}
}
#endif
/****************************************************************************
* Name: sam_adc_trigger
*
@ -1119,6 +1212,18 @@ static void sam_adc_trigger(struct sam_adc_s *priv)
sam_adc_putreg(priv, SAM_ADC_TRGR, regval);
#elif defined(CONFIG_SAMA5_ADC_TIOATRIG)
/* Start the timer */
#if defined(CONFIG_SAMA5_ADC_TIOA0TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN0);
#elif defined(CONFIG_SAMA5_ADC_TIOA1TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN1);
#elif defined(CONFIG_SAMA5_ADC_TIOA2TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN2);
#else
# error Timer/counter for trigger not defined
#endif
/* Configure to trigger using Timer/counter 0, channel 1, 2, or 3.
* NOTE: This trigger option depends on having properly configuer
* timer/counter 0 to provide this output. That is done independently

View File

@ -116,7 +116,7 @@
# define CAN_FREQUENCY (BOARD_MCK_FREQUENCY >> 3)
# define CAN_PCR_DIV PMC_PCR_DIV8
#else
# error Cannot realize ADC input frequency
# error Cannot realize CAN input frequency
#endif
/* Debug ********************************************************************/

View File

@ -57,15 +57,63 @@
#include <assert.h>
#include <errno.h>
#include <arch/board/board.h>
#include "up_arch.h"
#include "sam_periphclks.h"
#include "chip/sam_pmc.h"
#include "sam_pio.h"
#include "sam_tc.h"
#if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1)
/****************************************************************************
* 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
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes the static configuration of a TC channel */
struct sam_chconfig_s
{
uintptr_t base; /* Channel register base address */
pio_pinset_t clkset; /* CLK input PIO configuration */
pio_pinset_t tioaset; /* Output A PIO configuration */
pio_pinset_t tiobset; /* Output B PIO configuration */
};
/* This structure describes the static configuration of a TC */
struct sam_tcconfig_s
{
uintptr_t base; /* TC register base address */
uint8_t pid; /* Peripheral ID */
uint8_t chfirst; /* First channel number */
/* Channels */
struct sam_chconfig_s channel[3];
};
/* This structure describes one timer counter channel */
@ -134,14 +182,187 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel);
/****************************************************************************
* Private Data
****************************************************************************/
/* Static timer configuration */
#ifdef CONFIG_SAMA5_TC0
static const struct sam_tcconfig_s g_tc012config =
{
.base = SAM_TC012_VBASE,
.pid = SAM_PID_TC0,
.chfirst = 0,
.channel =
{
{
SAM_TC012_CHAN_BASE(0),
#ifdef CONFIG_SAMA5_TC0_CLK0
.clkset = PIO_TC0_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOA0
.tioaset = PIO_TC0_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOB0
.tiobset = PIO_TC0_IOB,
#else
.tiobset = 0,
#endif
},
{
SAM_TC012_CHAN_BASE(1),
#ifdef CONFIG_SAMA5_TC0_CLK1
.clkset = PIO_TC1_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOA1
.tioaset = PIO_TC1_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOB1
.tiobset = PIO_TC1_IOB,
#else
.tiobset = 0,
#endif
},
{
SAM_TC012_CHAN_BASE(2),
#ifdef CONFIG_SAMA5_TC0_CLK2
.clkset = PIO_TC2_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOA2
.tioaset = PIO_TC2_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC0_TIOB2
.tiobset = PIO_TC2_IOB,
#else
.tiobset = 0,
#endif
},
},
};
#endif
#ifdef CONFIG_SAMA5_TC1
static const struct sam_tcconfig_s g_tc345config =
{
.base = SAM_TC345_VBASE,
.pid = SAM_PID_TC1,
.chfirst = 3,
.channel =
{
{
SAM_TC345_CHAN_BASE(3),
#ifdef CONFIG_SAMA5_TC1_CLK3
.clkset = PIO_TC3_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOA3
.tioaset = PIO_TC3_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOB3
.tiobset = PIO_TC3_IOB,
#else
.tiobset = 0,
#endif
},
{
SAM_TC345_CHAN_BASE(4),
#ifdef CONFIG_SAMA5_TC1_CLK4
.clkset = PIO_TC4_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOA4
.tioaset = PIO_TC4_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOB4
.tiobset = PIO_TC4_IOB,
#else
.tiobset = 0,
#endif
},
{
SAM_TC345_CHAN_BASE(5),
#ifdef CONFIG_SAMA5_TC1_CLK5
.clkset = PIO_TC5_CLK,
#else
.clkset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOA5
.tioaset = PIO_TC5_IOA,
#else
.tioaset = 0,
#endif
#ifdef CONFIG_SAMA5_TC1_TIOB5
.tiobset = PIO_TC5_IOB,
#else
.tiobset = 0,
#endif
},
},
};
#endif
/* Timer/counter state */
#ifdef CONFIG_SAMA5_TC0
static struct sam_tc_s g_tc012;
#endif
#ifdef CONFIG_SAMA5_TC1
static struct sam_tc_s g_tc345;
#endif
/* TC frequency data. This table provides the frequency for each selection of TCCLK */
#define TC_NDIVIDERS 5
/* This is the list of divider values */
static const uint16_t g_divider[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 */
};
/* TC register lookup used by sam_tc_setregister */
#define TC_NREGISTERS 3
static const uint8_t g_regoffset[TC_NREGISTERS] =
{
SAM_TC_RA_OFFSET, /* Register A */
SAM_TC_RB_OFFSET, /* Register B */
SAM_TC_RC_OFFSET /* Register C */
};
/****************************************************************************
* Private Functions
****************************************************************************/
@ -352,14 +573,13 @@ 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 struct sam_tc_s *tc;
static struct sam_chan_s *chan;
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;
irqstate_t flags;
uintptr_t tcbase;
uintptr_t chbase;
int chfirst;
int chndx;
int pid;
uint32_t regval;
uint8_t ch;
int i;
/* Select the timer/counter and get the index associated with the
@ -369,34 +589,16 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
#ifdef CONFIG_SAMA5_TC0
if (channel >= 0 && channel < 3)
{
tc = &g_tc012;
chndx = channel;
/* These are only needed in the case where we need to initialize the
* timer/counter.
*/
chfirst = 0;
tcbase = SAM_TC012_VBASE;
chbase = SAM_TC012_CHAN_BASE(channel);
pid = SAM_PID_TC0;
tc = &g_tc012;
tcconfig = &g_tc012config;
}
else
#endif
#ifdef CONFIG_SAMA5_TC1
if (channel >= 3 && channel < 5)
{
tc = &g_tc345;
chndx = channel - 3;
/* These are only needed in the case where we need to initialize the
* timer/counter.
*/
chfirst = 3;
tcbase = SAM_TC345_VBASE;
chbase = SAM_TC345_CHAN_BASE(channel)
pid = SAM_PID_TC0;
tc = &g_tc345;
tcconfig = &g_tc345config;
}
else
#endif
@ -417,21 +619,53 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
memset(tc, 0, sizeof(struct sam_tc_s));
sem_init(&tc->exclsem, 0, 1);
tc->base = tcbase;
tc->pid = pid;
tc->base = tcconfig->base;
tc->pid = tcconfig->pid;
/* Initialize the channels */
for (i = 0; i < 3; i++)
for (i = 0, ch = tcconfig->chfirst; i < SAM_TC_NCHANNELS; i++)
{
/* Initialize the channel data structure */
chan = &tc->channel[i];
chan->base = chbase;
chan->chan = chfirst++;
chconfig = &tcconfig->channel[i];
chan->base = chconfig->base;
chan->chan = ch++;
/* Configure channel input/output pins */
if (chconfig->clkset)
{
/* Configure clock input pin */
sam_configpio(chconfig->clkset);
}
if (chconfig->tioaset)
{
/* Configure output A pin */
sam_configpio(chconfig->tioaset);
}
if (chconfig->tiobset)
{
/* Configure output B pin */
sam_configpio(chconfig->tiobset);
}
}
/* Set the maximum TC peripheral clock frequency */
regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | TC_PCR_DIV | PMC_PCR_EN;
putreg32(regval, SAM_PMC_PCR);
/* Enable clocking to the timer counter */
sam_enableperiph0(pid);
sam_enableperiph0(tcconfig->pid);
/* Now the channel is initialized */
@ -445,7 +679,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
/* Get the requested channel structure */
chan = &tc->channel[chndx];
chan = &tc->channel[channel - tcconfig->chfirst];
/* Is it available? */
@ -484,7 +718,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
*
****************************************************************************/
TCHANDLE sam_tc_allocate(int channel, int mode)
TC_HANDLE sam_tc_allocate(int channel, int mode)
{
struct sam_chan_s *chan;
@ -514,7 +748,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode)
/* Return an opaque reference to the channel */
return (TCHANDLE)chan;
return (TC_HANDLE)chan;
}
/****************************************************************************
@ -531,7 +765,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode)
*
****************************************************************************/
void sam_tc_free(TCHANDLE handle)
void sam_tc_free(TC_HANDLE handle)
{
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
DEBUGASSERT(chan && chan->inuse);
@ -559,7 +793,7 @@ void sam_tc_free(TCHANDLE handle)
*
****************************************************************************/
void sam_tc_start(TCHANDLE handle)
void sam_tc_start(TC_HANDLE handle)
{
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
@ -580,7 +814,7 @@ void sam_tc_start(TCHANDLE handle)
*
****************************************************************************/
void sam_tc_stop(TCHANDLE handle)
void sam_tc_stop(TC_HANDLE handle)
{
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
@ -588,6 +822,53 @@ void sam_tc_stop(TCHANDLE handle)
sam_chan_putreg(chan, SAM_TC_CCR_OFFSET, TC_CCR_CLKDIS);
}
/****************************************************************************
* Name: sam_tc_setregister
*
* Description:
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
* setting in the regsiter will be the TC input frequency divided by
* the provided divider (which should derive from the divider returned
* by sam_tc_divider).
*
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
* None
*
****************************************************************************/
void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
{
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
DEBUGASSERT(reg < TC_NREGISTERS);
sam_chan_putreg(chan, g_regoffset[reg], TC_FREQUENCY / 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.
* This value needed for
*
* Input Parameters:
* None
*
* Returned Value:
* The timer input frequency.
*
****************************************************************************/
uint32_t sam_tc_frequency(void)
{
return TC_FREQUENCY;
}
/****************************************************************************
* Name: sam_tc_divisor
*
@ -595,17 +876,17 @@ void sam_tc_stop(TCHANDLE handle)
* Finds the best MCK divisor given the timer frequency and MCK. The
* result is guaranteed to satisfy the following equation:
*
* (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
*
* with DIV being the highest possible value.
* where:
* freq - the desitred frequency
* Ftc - The timer/counter input frequency
* div - With DIV being the highest possible value.
*
* Input Parameters:
*
* frequency Desired timer frequency.
* mck Master clock frequency.
* div Divisor value.
* tcclks TCCLKS field value for divisor.
* boardmck Board clock frequency.
*
* Returned Value:
* Zero (OK) if a proper divisor has been found, otherwise a negated errno
@ -613,46 +894,41 @@ void sam_tc_stop(TCHANDLE handle)
*
****************************************************************************/
uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div,
uint32_t *tcclks, uint32_t boardmck)
int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
{
const uint32_t adivisors[5] = { 2, 8, 32, 128, boardmck / 32768 };
int ndx = 0;
/* Satisfy lower bound */
while (frequency < ((mck / adivisors[ndx]) / 65536))
while (frequency < (g_divfreq[ndx] >> 16))
{
ndx++;
/* If no divisor can be found, return -ERANGE */
if (ndx == (sizeof(adivisors)/sizeof(adivisors[0])))
if (++ndx > TC_NDIVIDERS)
{
/* If no divisor can be found, return -ERANGE */
return -ERANGE;
}
}
/* Try to maximize DIV while satisfying upper bound */
/* Try to maximize DIV while still satisfying upper bound */
while (ndx < 4)
for (; ndx < (TC_NDIVIDERS-1); ndx++)
{
if (frequency > (mck / adivisors[ndx + 1]))
if (frequency > g_divfreq[ndx + 1])
{
break;
}
ndx++;
}
/* Store results */
/* Return the divider value */
if (div)
{
*div = adivisors[ndx];
*div = g_divider[ndx];
}
/* REturn the TCCLKS selection */
if (tcclks)
{
*tcclks = ndx;

View File

@ -62,11 +62,17 @@
#define TC_CHAN4 4
#define TC_CHAN5 5
/* Register identifier used with sam_tc_setregister */
#define TC_REGA 0
#define TC_REGB 1
#define TC_REGC 2
/****************************************************************************
* Public Types
****************************************************************************/
typedef void *TCHANDLE;
typedef void *TC_HANDLE;
/****************************************************************************
* Public Data
@ -104,7 +110,7 @@ extern "C"
*
****************************************************************************/
TCHANDLE sam_tc_allocate(int channel, int mode);
TC_HANDLE sam_tc_allocate(int channel, int mode);
/****************************************************************************
* Name: sam_tc_free
@ -120,7 +126,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode);
*
****************************************************************************/
void sam_tc_free(TCHANDLE handle);
void sam_tc_free(TC_HANDLE handle);
/****************************************************************************
* Name: sam_tc_start
@ -136,7 +142,7 @@ void sam_tc_free(TCHANDLE handle);
*
****************************************************************************/
void sam_tc_start(TCHANDLE handle);
void sam_tc_start(TC_HANDLE handle);
/****************************************************************************
* Name: sam_tc_stop
@ -151,7 +157,44 @@ void sam_tc_start(TCHANDLE handle);
*
****************************************************************************/
void sam_tc_stop(TCHANDLE handle);
void sam_tc_stop(TC_HANDLE handle);
/****************************************************************************
* Name: sam_tc_setregister
*
* Description:
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
* setting in the regsiter will be the TC input frequency divided by
* the provided divider (which should derive from the divider returned
* by sam_tc_divider).
*
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
* None
*
****************************************************************************/
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.
*
* Input Parameters:
* None
*
* Returned Value:
* The timer input frequency.
*
****************************************************************************/
uint32_t sam_tc_frequency(void);
/****************************************************************************
* Name: sam_tc_divisor
@ -160,17 +203,17 @@ void sam_tc_stop(TCHANDLE handle);
* Finds the best MCK divisor given the timer frequency and MCK. The
* result is guaranteed to satisfy the following equation:
*
* (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
*
* with DIV being the highest possible value.
* where:
* freq - the desitred frequency
* Ftc - The timer/counter input frequency
* div - With DIV being the highest possible value.
*
* Input Parameters:
*
* frequency Desired timer frequency.
* mck Master clock frequency.
* div Divisor value.
* tcclks TCCLKS field value for divisor.
* boardmck Board clock frequency.
*
* Returned Value:
* Zero (OK) if a proper divisor has been found, otherwise a negated errno
@ -178,8 +221,7 @@ void sam_tc_stop(TCHANDLE handle);
*
****************************************************************************/
uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div,
uint32_t *tcclks, uint32_t boardmck);
int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks);
#undef EXTERN
#ifdef __cplusplus