From 319ad528cdd744804b9e94c1ba072c69fc127eac Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 17 Aug 2016 12:34:54 -0600 Subject: [PATCH] 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 5d5851a5cd76b780f2caab974ebe39ded86ee9e0. --- README.txt | 6 +- arch/arm/src/samv7/sam_dac.c | 25 ++-- arch/arm/src/samv7/sam_freerun.c | 19 ++- arch/arm/src/samv7/sam_oneshot.c | 19 ++- arch/arm/src/samv7/sam_tc.c | 214 +++++++++++++++++++++---------- arch/arm/src/samv7/sam_tc.h | 23 +++- 6 files changed, 202 insertions(+), 104 deletions(-) diff --git a/README.txt b/README.txt index 108c5cd324..3617f874a7 100644 --- a/README.txt +++ b/README.txt @@ -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, diff --git a/arch/arm/src/samv7/sam_dac.c b/arch/arm/src/samv7/sam_dac.c index 52443523d1..598201ce39 100644 --- a/arch/arm/src/samv7/sam_dac.c +++ b/arch/arm/src/samv7/sam_dac.c @@ -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 */ diff --git a/arch/arm/src/samv7/sam_freerun.c b/arch/arm/src/samv7/sam_freerun.c index 9374409b88..4db9511218 100644 --- a/arch/arm/src/samv7/sam_freerun.c +++ b/arch/arm/src/samv7/sam_freerun.c @@ -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 diff --git a/arch/arm/src/samv7/sam_oneshot.c b/arch/arm/src/samv7/sam_oneshot.c index 5506d8ff8a..bc9c23b515 100644 --- a/arch/arm/src/samv7/sam_oneshot.c +++ b/arch/arm/src/samv7/sam_oneshot.c @@ -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 diff --git a/arch/arm/src/samv7/sam_tc.c b/arch/arm/src/samv7/sam_tc.c index 6f93458d8a..691366af8f 100644 --- a/arch/arm/src/samv7/sam_tc.c +++ b/arch/arm/src/samv7/sam_tc.c @@ -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 */ diff --git a/arch/arm/src/samv7/sam_tc.h b/arch/arm/src/samv7/sam_tc.h index a2e25fdf87..d62ac21252 100644 --- a/arch/arm/src/samv7/sam_tc.h +++ b/arch/arm/src/samv7/sam_tc.h @@ -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