ZNeo: Calculation of timer reload and prescaler was wrong

This commit is contained in:
Gregory Nutt 2014-04-25 15:38:00 -06:00
parent b261a9886a
commit 3a2b1b0060
2 changed files with 547 additions and 449 deletions

File diff suppressed because it is too large Load Diff

View File

@ -43,19 +43,31 @@
#include <debug.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include "chip/chip.h"
#include "clock_internal.h"
#include "up_internal.h"
/***************************************************************************
* Definitions
* Pre-processor Definitions
***************************************************************************/
/* The desired timer interrupt frequency is provided by the definition
* CLOCKS_PER_SEC (see include/time.h). CLOCKS_PER_SEC defines the desired
* number of system clock ticks per second. That value is a user
* configurable setting that defaults to 100 (100 ticks per second = 10 MS
* interval).
*
* The RCC feeds the Cortex System Timer (SysTick) with the AHB clock (HCLK)
* divided by 8. The SysTick can work either with this clock or with the
* Cortex clock (HCLK), configurable in the SysTick Control and Status
* register.
*/
/* System clock frequency value from ZDS target settings */
extern _Erom unsigned long SYS_CLK_FREQ;
#define _DEFCLK ((unsigned long)&SYS_CLK_FREQ)
extern _Erom uint8_t SYS_CLK_FREQ;
#define _DEFCLK ((uint32_t)&SYS_CLK_FREQ)
/***************************************************************************
* Private Types
@ -97,39 +109,125 @@ int up_timerisr(int irq, uint32_t *regs)
void up_timerinit(void)
{
uint32_t reload;
uint32_t scaledfreq;
uint32_t rawdiv;
uint8_t divider;
uint8_t regval;
int shift;
up_disable_irq(Z16F_IRQ_SYSTIMER);
/* Disable the timer and configure for divide by 1 and continuous mode. */
putreg8( Z16F_TIMERSCTL1_DIV1 | Z16F_TIMERSCTL1_CONT, Z16F_TIMER0_CTL1);
regval = Z16F_TIMERSCTL1_DIV1 | Z16F_TIMERSCTL1_CONT;
putreg8(regval, Z16F_TIMER0_CTL1);
/* Assign an intial timer value */
/* Assign an initial timer value */
putreg16(0x0001, Z16F_TIMER0_HL);
/* Set the timer reload value.
*
* In continuous mode:
/* Calculate timer reload value (continuous mode)
*
* timer_period = reload_value * divisor / system_clock_freqency
* timer_frequency = system_clock_freqency / divisor / reload_value
* or
* reload_value = (system_clock_frequency / timer_frequency / divisor
*
* For system_clock_frequency=200MHz, timer_frequency=100KHz, and divisor=1,
* this yields 200.
* The prescale value ranges from 1 to 128, the reload value must be less
* then or equal to 0xffff. We would like to select the smallest prescaler
* value and the largest reload value for the greatest accuracy.
*
* Example: system_clock_frequency=20MHz, timer_frequency=100Hz:
* scaledfreq = 20,000,000 / 100
* = 200,000
* rawdiv = (200,000 >> 16) + 1
* = 4
* divider = Z16F_TIMERSCTL1_DIV4
* shift = 2
* reload = 200,000 >> 2
* = 50,000
*
* Example: system_clock_frequency=18.432MHz, timer_frequency=100Hz:
* scaledfreq = 20,000,000 / 100
* = 200,000
* divisor = ((18,432,000 / 100) >> 16) + 1
* = 3 -> 4 (need to to up to next power of two)
* reload_value = 20,000,000 / 100 / 4
* = 56,080
*/
putreg16(((uint32_t)_DEFCLK / 100000), Z16F_TIMER0_R);
#if 0 /* Does not work ??? */
scaledfreq = _DEFCLK / CLOCKS_PER_SEC;
#else
scaledfreq = (BOARD_SYSTEM_FREQUENCY / CLOCKS_PER_SEC);
#endif
rawdiv = (scaledfreq >> 16) + 1;
if (rawdiv < 2)
{
divider = Z16F_TIMERSCTL1_DIV1;
shift = 0;
}
else if (rawdiv < 3)
{
divider = Z16F_TIMERSCTL1_DIV2;
shift = 1;
}
else if (rawdiv < 7)
{
divider = Z16F_TIMERSCTL1_DIV4;
shift = 2;
}
else if (rawdiv < 15)
{
divider = Z16F_TIMERSCTL1_DIV8;
shift = 3;
}
else if (rawdiv < 31)
{
divider = Z16F_TIMERSCTL1_DIV16;
shift = 4;
}
else if (rawdiv < 63)
{
divider = Z16F_TIMERSCTL1_DIV32;
shift = 5;
}
else if (rawdiv < 127)
{
divider = Z16F_TIMERSCTL1_DIV64;
shift = 6;
}
else
{
divider = Z16F_TIMERSCTL1_DIV128;
shift = 7;
}
reload = scaledfreq >> shift;
DEBUGASSERT(reload <= 0xffff);
/* Set the timer reload value */
putreg16((uint16_t)reload, Z16F_TIMER0_R);
/* Set the prescale value */
regval = getreg8(Z16F_TIMER0_CTL1);
regval &= ~Z16F_TIMERSCTL1_DIVMASK;
regval |= divider;
putreg8(regval, Z16F_TIMER0_CTL1);
/* Enable the timer */
putreg8((getreg8(Z16F_TIMER0_CTL1) | Z16F_TIMERCTL1_TEN), Z16F_TIMER0_CTL1);
regval |= Z16F_TIMERCTL1_TEN;
putreg8(regval, Z16F_TIMER0_CTL1);
/* Set the timer priority */
/* Attach and enable the timer interrupt (leaving at priority 0 */
/* Attach and enable the timer interrupt (leaving at priority 0) */
irq_attach(Z16F_IRQ_SYSTIMER, (xcpt_t)up_timerisr);
up_enable_irq(Z16F_IRQ_SYSTIMER);
}