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:
Gregory Nutt 2016-08-17 12:34:54 -06:00
parent 0c2cde73f9
commit 319ad528cd
6 changed files with 202 additions and 104 deletions

View File

@ -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,

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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