Revert "sam_tc_clockselect() reworked to calculate frequency error using smallest possible divisor minimizing the frequency error rather than largest possible divisor which maximized the error."
This reverts commit 5d5851a5cd
.
This commit is contained in:
parent
0c2cde73f9
commit
319ad528cd
@ -908,9 +908,9 @@ Build Targets and Options
|
||||
|
||||
distclean
|
||||
|
||||
Does 'clean' then also removes all configuration, dependency, and
|
||||
other context files. This essentially restores the directory structure
|
||||
to its original, unconfigured stated.
|
||||
Does 'clean' then also removes all configuration and context files.
|
||||
This essentially restores the directory structure to its original,
|
||||
unconfigured stated.
|
||||
|
||||
Application housekeeping targets. The APPDIR variable refers to the user
|
||||
application directory. A sample apps/ directory is included with NuttX,
|
||||
|
@ -373,8 +373,7 @@ static int dac_timer_init(struct sam_dac_s *priv, uint32_t freq_required,
|
||||
int channel)
|
||||
{
|
||||
uint32_t mode;
|
||||
uint32_t tcclks;
|
||||
uint32_t div;
|
||||
uint32_t regval;
|
||||
uint32_t freq_actual;
|
||||
|
||||
ainfo("required frequency=%ld [Hz], channel=%d\n",
|
||||
@ -382,11 +381,15 @@ static int dac_timer_init(struct sam_dac_s *priv, uint32_t freq_required,
|
||||
|
||||
DEBUGASSERT(priv && (freq_required > 0) && (channel >= 0 && channel <= 2));
|
||||
|
||||
/* Calculate the best possible clock source and clock divisor value */
|
||||
/* Set the timer/counter waveform mode the the clock input. Use smallest
|
||||
* MCK divisor of 8 to have highest clock resolution thus smallest frequency
|
||||
* error. With 32 bit counter the lowest possible frequency of 1 Hz is easily
|
||||
* supported.
|
||||
*/
|
||||
|
||||
freq_actual = sam_tc_clockselect(freq_required, &tcclks, &div);
|
||||
/* TODO Add support for TC_CMR_TCCLKS_PCK6 to reduce frequency error */
|
||||
|
||||
mode = (tcclks | /* Use MCK/8 clock signal */
|
||||
mode = (TC_CMR_TCCLKS_MCK8 | /* Use MCK/8 clock signal */
|
||||
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 */
|
||||
@ -401,17 +404,21 @@ static int dac_timer_init(struct sam_dac_s *priv, uint32_t freq_required,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set up clock divisor */
|
||||
/* Calculate the actual counter value from this divider and the tc input
|
||||
* frequency.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(div >= 2); /* Minimum divider required by implementation */
|
||||
regval = BOARD_MCK_FREQUENCY / 8 / freq_required;
|
||||
DEBUGASSERT(regval > 0); /* Will check for integer underflow */
|
||||
|
||||
/* Set up TC_RA and TC_RC. The frequency is determined by RA and RC:
|
||||
* TIOA is cleared on RA match; TIOA is set on RC match.
|
||||
*/
|
||||
|
||||
sam_tc_setregister(priv->tc, TC_REGA, div >> 1);
|
||||
sam_tc_setregister(priv->tc, TC_REGC, div);
|
||||
sam_tc_setregister(priv->tc, TC_REGA, regval >> 1);
|
||||
sam_tc_setregister(priv->tc, TC_REGC, regval);
|
||||
|
||||
freq_actual = BOARD_MCK_FREQUENCY / 8 / regval;
|
||||
ainfo("configured frequency=%ld [Hz]\n", (long)freq_actual);
|
||||
|
||||
/* And start the timer */
|
||||
|
@ -119,24 +119,29 @@ static void sam_freerun_handler(TC_HANDLE tch, void *arg, uint32_t sr)
|
||||
int sam_freerun_initialize(struct sam_freerun_s *freerun, int chan,
|
||||
uint16_t resolution)
|
||||
{
|
||||
uint32_t freq_required;
|
||||
uint32_t freq_actual;
|
||||
uint32_t div;
|
||||
uint32_t frequency;
|
||||
uint32_t actual;
|
||||
uint32_t cmr;
|
||||
int ret;
|
||||
|
||||
tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
|
||||
DEBUGASSERT(freerun && resolution > 0);
|
||||
|
||||
/* Get the TC frequency the corresponds to the requested resolution */
|
||||
|
||||
freq_required = USEC_PER_SEC / (uint32_t)resolution;
|
||||
frequency = USEC_PER_SEC / (uint32_t)resolution;
|
||||
|
||||
/* The pre-calculate values to use when we start the timer */
|
||||
|
||||
freq_actual = sam_tc_clockselect(freq_required, &cmr, &div);
|
||||
ret = sam_tc_clockselect(frequency, &cmr, &actual);
|
||||
if (ret < 0)
|
||||
{
|
||||
tmrerr("ERROR: sam_tc_clockselect failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tmrinfo("freq required=%lu, freq actual=%lu, TC_CMR.TCCLKS=%08lx\n",
|
||||
(unsigned long)freq_required, (unsigned long)freq_actual,
|
||||
tmrinfo("frequency=%lu, actual=%lu, cmr=%08lx\n",
|
||||
(unsigned long)frequency, (unsigned long)actual,
|
||||
(unsigned long)cmr);
|
||||
|
||||
/* Allocate the timer/counter and select its mode of operation
|
||||
|
@ -147,24 +147,29 @@ static void sam_oneshot_handler(TC_HANDLE tch, void *arg, uint32_t sr)
|
||||
int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan,
|
||||
uint16_t resolution)
|
||||
{
|
||||
uint32_t freq_required;
|
||||
uint32_t freq_actual;
|
||||
uint32_t div;
|
||||
uint32_t frequency;
|
||||
uint32_t actual;
|
||||
uint32_t cmr;
|
||||
int ret;
|
||||
|
||||
tmrinfo("chan=%d resolution=%d usec\n", chan, resolution);
|
||||
DEBUGASSERT(oneshot && resolution > 0);
|
||||
|
||||
/* Get the TC frequency the corresponds to the requested resolution */
|
||||
|
||||
freq_required = USEC_PER_SEC / (uint32_t)resolution;
|
||||
frequency = USEC_PER_SEC / (uint32_t)resolution;
|
||||
|
||||
/* The pre-calculate values to use when we start the timer */
|
||||
|
||||
freq_actual = sam_tc_clockselect(freq_required, &cmr, &div);
|
||||
ret = sam_tc_clockselect(frequency, &cmr, &actual);
|
||||
if (ret < 0)
|
||||
{
|
||||
tmrerr("ERROR: sam_tc_clockselect failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tmrinfo("freq required=%lu, freq actual=%lu, TC_CMR.TCCLKS=%08lx\n",
|
||||
(unsigned long)freq_required, (unsigned long)freq_actual,
|
||||
tmrinfo("frequency=%lu, actual=%lu, cmr=%08lx\n",
|
||||
(unsigned long)frequency, (unsigned long)actual,
|
||||
(unsigned long)cmr);
|
||||
|
||||
/* Allocate the timer/counter and select its mode of operation
|
||||
|
@ -213,8 +213,8 @@ static int sam_tc11_interrupt(int irq, void *context);
|
||||
|
||||
static uint32_t sam_tc_mckfreq_lookup(uint32_t ftcin, int ndx);
|
||||
static inline uint32_t sam_tc_tcclks_lookup(int ndx);
|
||||
static uint32_t sam_tc_freq_err_abs(uint32_t freq_required,
|
||||
uint32_t freq_input, uint32_t *div);
|
||||
static int sam_tc_mcksrc(uint32_t frequency, uint32_t *tcclks,
|
||||
uint32_t *actual);
|
||||
static inline struct sam_chan_s *sam_tc_initialize(int channel);
|
||||
|
||||
/****************************************************************************
|
||||
@ -1019,51 +1019,83 @@ static inline uint32_t sam_tc_tcclks_lookup(int ndx)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sam_tc_freq_err_abs
|
||||
* Name: sam_tc_mcksrc
|
||||
*
|
||||
* Description:
|
||||
* Calculate best possible frequency error given input frequency and
|
||||
* required frequency knowing that input frequency can be divided by
|
||||
* integer divisor. The divisor for which the given error was calculated
|
||||
* is also returned.
|
||||
* Finds the best MCK divisor given the timer frequency and MCK. The
|
||||
* result is guaranteed to satisfy the following equation:
|
||||
*
|
||||
* (Ftcin / (div * 65536)) <= freq <= (Ftcin / div)
|
||||
*
|
||||
* where:
|
||||
* freq - the desired frequency
|
||||
* Ftcin - The timer/counter input frequency
|
||||
* div - With DIV being the highest possible value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* freq_required Desired timer frequency
|
||||
* freq_input TC module input frequency
|
||||
* div Pointer to the divisor for which the error was
|
||||
* calculated
|
||||
* frequency Desired timer frequency.
|
||||
* tcclks TCCLKS field value for divisor.
|
||||
* actual The actual freqency of the MCK
|
||||
*
|
||||
* Returned Value:
|
||||
* Absolute value of the smallest possible frequency error
|
||||
* Zero (OK) if a proper divisor has been found, otherwise a negated errno
|
||||
* value indicating the nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint32_t sam_tc_freq_err_abs(uint32_t freq_required, uint32_t freq_input,
|
||||
uint32_t *div)
|
||||
static int sam_tc_mcksrc(uint32_t frequency, uint32_t *tcclks,
|
||||
uint32_t *actual)
|
||||
{
|
||||
uint32_t freq_actual;
|
||||
uint32_t freq_error;
|
||||
uint32_t fselect;
|
||||
uint32_t fnext;
|
||||
int ndx = 0;
|
||||
|
||||
DEBUGASSERT(freq_input >= freq_required);
|
||||
DEBUGASSERT(UINT32_MAX - freq_required/2 > freq_input);
|
||||
tmrinfo("frequency=%d\n", frequency);
|
||||
|
||||
/* Integer division will truncate result toward zero, make sure the result
|
||||
* is rounded instead.
|
||||
/* Satisfy lower bound. That is, the value of the divider such that:
|
||||
*
|
||||
* frequency >= (tc_input_frequency * 65536) / divider.
|
||||
*/
|
||||
|
||||
*div = (freq_input + freq_required/2) / freq_required;
|
||||
freq_actual = freq_input / *div;
|
||||
|
||||
if (freq_required >= freq_actual)
|
||||
for (; ndx < TC_NDIVIDERS; ndx++)
|
||||
{
|
||||
freq_error = freq_required - freq_actual;
|
||||
}
|
||||
else
|
||||
{
|
||||
freq_error = freq_actual - freq_required;
|
||||
fselect = sam_tc_mckfreq_lookup(BOARD_MCK_FREQUENCY, ndx);
|
||||
if (frequency >= (fselect >> 16))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return freq_error;
|
||||
if (ndx >= TC_NDIVIDERS)
|
||||
{
|
||||
/* If no divisor can be found, return -ERANGE */
|
||||
|
||||
tmrerr("ERROR: Lower bound search failed\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* 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; ndx++)
|
||||
{
|
||||
fnext = sam_tc_mckfreq_lookup(BOARD_MCK_FREQUENCY, ndx + 1);
|
||||
if (frequency > fnext)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fselect = fnext;
|
||||
}
|
||||
|
||||
/* Return the actual frequency and the TCCLKS selection */
|
||||
|
||||
*actual = fselect;
|
||||
*tcclks = sam_tc_tcclks_lookup(ndx);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1612,80 +1644,120 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle)
|
||||
* Name: sam_tc_clockselect
|
||||
*
|
||||
* Description:
|
||||
* Finds the best clock source and clock divisor to configure required
|
||||
* frequency.
|
||||
* Finds the best MCK divisor given the timer frequency and MCK. The
|
||||
* result is guaranteed to satisfy the following equation:
|
||||
*
|
||||
* (Ftcin / (div * 65536)) <= freq <= (Ftcin / div)
|
||||
*
|
||||
* where:
|
||||
* freq - the desired frequency
|
||||
* Ftcin - The timer/counter input frequency
|
||||
* div - With DIV being the highest possible value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* frequency Desired timer frequency
|
||||
* tcclks TC_CMRx.TCCLKS bit field (clock selection) value
|
||||
* div The divisor value to be configured for the TC
|
||||
* frequency Desired timer frequency.
|
||||
* tcclks TCCLKS field value for divisor.
|
||||
* actual The actual freqency of the MCK
|
||||
*
|
||||
* Returned Value:
|
||||
* Rhe actual frequency which will be configured with calculated
|
||||
* parameters
|
||||
* Zero (OK) if a proper divisor has been found, otherwise a negated errno
|
||||
* value indicating the nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
uint32_t sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks,
|
||||
uint32_t *div)
|
||||
int sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks,
|
||||
uint32_t *actual)
|
||||
{
|
||||
uint32_t mck8_freq;
|
||||
uint32_t mck8_error;
|
||||
uint32_t tcclks_select;
|
||||
uint32_t div_select;
|
||||
uint32_t freq_actual;
|
||||
uint32_t mck_actual;
|
||||
uint32_t mck_tcclks;
|
||||
uint32_t mck_error;
|
||||
int ret;
|
||||
|
||||
/* Calculate frequency error for MCK clock. Use smallest possible MCK
|
||||
* divisor of 8 to have highest clock resolution and thus smallest
|
||||
* frequency error. With 32 bit counter the lowest possible frequency
|
||||
* of 1 Hz is easily supported.
|
||||
*/
|
||||
/* Try to satisfy the requested frequency with the MCK or slow clock */
|
||||
|
||||
mck8_freq = BOARD_MCK_FREQUENCY/8;
|
||||
mck8_error = sam_tc_freq_err_abs(frequency, mck8_freq, &div_select);
|
||||
tcclks_select = TC_CMR_TCCLKS_MCK8;
|
||||
freq_actual = mck8_freq / div_select;
|
||||
ret = sam_tc_mcksrc(frequency, &mck_tcclks, &mck_actual);
|
||||
if (ret < 0)
|
||||
{
|
||||
mck_error = UINT32_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the absolute value of the frequency error */
|
||||
|
||||
if (mck_actual > frequency)
|
||||
{
|
||||
mck_error = mck_actual - frequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
mck_error = frequency - mck_actual;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we do better with PCK6 */
|
||||
|
||||
if (sam_pck_isenabled(PCK6))
|
||||
{
|
||||
uint32_t pck6_freq;
|
||||
uint32_t pck6_actual;
|
||||
uint32_t pck6_error;
|
||||
uint32_t pck6_div;
|
||||
|
||||
/* Get the absolute value of the frequency error */
|
||||
|
||||
pck6_freq = sam_pck_frequency(PCK6);
|
||||
pck6_error = sam_tc_freq_err_abs(frequency, pck6_freq, &pck6_div);
|
||||
pck6_actual = sam_pck_frequency(PCK6);
|
||||
if (pck6_actual > frequency)
|
||||
{
|
||||
pck6_error = pck6_actual - frequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
pck6_error = frequency - pck6_actual;
|
||||
}
|
||||
|
||||
/* Return the PCK6 selection if the error is smaller */
|
||||
|
||||
if (pck6_error < mck8_error)
|
||||
if (pck6_error < mck_error)
|
||||
{
|
||||
tcclks_select = TC_CMR_TCCLKS_PCK6;
|
||||
div_select = pck6_div;
|
||||
freq_actual = pck6_freq / pck6_div;
|
||||
/* Return the PCK selection */
|
||||
|
||||
if (actual)
|
||||
{
|
||||
tmrinfo("return actual=%lu\n", (unsigned long)fselect);
|
||||
*actual = pck6_actual;
|
||||
}
|
||||
|
||||
/* Return the TCCLKS selection */
|
||||
|
||||
if (tcclks)
|
||||
{
|
||||
tmrinfo("return tcclks=%08lx\n", (unsigned long)TC_CMR_TCCLKS_PCK6);
|
||||
*tcclks = TC_CMR_TCCLKS_PCK6;
|
||||
}
|
||||
|
||||
/* Return success */
|
||||
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the MCK/slow clock selection */
|
||||
|
||||
if (actual)
|
||||
{
|
||||
tmrinfo("return actual=%lu\n", (unsigned long)mck_actual);
|
||||
*actual = mck_actual;
|
||||
}
|
||||
|
||||
/* Return the TCCLKS selection */
|
||||
|
||||
if (tcclks)
|
||||
{
|
||||
tmrinfo("return tcclks=%08lx\n", (unsigned long)tcclks_select);
|
||||
*tcclks = tcclks_select;
|
||||
tmrinfo("return tcclks=%08lx\n", (unsigned long)mck_tcclks);
|
||||
*tcclks = mck_tcclks;
|
||||
}
|
||||
|
||||
/* Return the divider value */
|
||||
/* Return success */
|
||||
|
||||
if (div)
|
||||
{
|
||||
tmrinfo("return div=%lu\n", (unsigned long)div_select);
|
||||
*div = div_select;
|
||||
}
|
||||
|
||||
return freq_actual;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SAMV7_TC0 || CONFIG_SAMV7_TC1 || CONFIG_SAMV7_TC2 || CONFIG_SAMV7_TC3 */
|
||||
|
@ -319,20 +319,29 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle);
|
||||
* Name: sam_tc_clockselect
|
||||
*
|
||||
* Description:
|
||||
* Finds the best clock source and clock divisor to configure required
|
||||
* frequency.
|
||||
* Finds the best MCK divisor given the timer frequency and MCK. The
|
||||
* result is guaranteed to satisfy the following equation:
|
||||
*
|
||||
* (Ftcin / (div * 65536)) <= freq <= (Ftcin / div)
|
||||
*
|
||||
* where:
|
||||
* freq - the desired frequency
|
||||
* Ftcin - The timer/counter input frequency
|
||||
* div - With DIV being the highest possible value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* frequency desired timer frequency
|
||||
* tcclks TC_CMRx.TCCLKS bit field (clock selection) value
|
||||
* div the divisor value to be configured for the TC
|
||||
* frequency Desired timer frequency.
|
||||
* tcclks TCCLKS field value for divisor.
|
||||
* actual The actual freqency of the MCK
|
||||
*
|
||||
* Returned Value:
|
||||
* the actual frequency which will be configured with calculated parameters
|
||||
* Zero (OK) if a proper divisor has been found, otherwise a negated errno
|
||||
* value indicating the nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
uint32_t sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks, uint32_t *div);
|
||||
int sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks,
|
||||
uint32_t *actual);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
|
Loading…
Reference in New Issue
Block a user