At 05:02 PM 12/5/03 -0500, you wrote:
>At 09:26 AM 12/5/03 -0800, you wrote:
> >I had looked at the timers before but it was unclear if you would have to
> >give up GPIO pins by putting them into alternate function mode (we're
> >already pin-constricted). I'm hoping for something with this type of
> >functionality but that doesn't require giving up such vital (to us)
> >resources as i/o pins. Care to guess whether the match registers could be
> >used without putting the i/o pins into alternate function mode?
>
>I would be surprised it they couldn't be used that way. I see two ways of
>doing it. One is to use interrupts on match. The other is to poll the
>status bit in the external match register (That's what I'm trying first).
A timer using timer 0. This is a first pass and still could use some
refinement and verification but it does show a method that works. Using
the instrumented set and clear in the wait routine (last routine) I get an
11.4 uS delay when asking for 10uS which fits with the bit toggle time I
measured earlier. It works fine down to 1uS and fails at 0.1uS. I haven't
delved further to determine just what the minimum wait time is. If you
want to time really short event (< a few microseconds) you would probably
want to perform the wait operation in-line rather than make a function call
otherwise the overhead becomes significant.
The real guts are in wait_ns, everything else is setup or support.
This is a polling version (no interrupts) that should keep time as long as
a wait or time gathering call is made every minute and a half or so. Other
than that there is no extra runtime overhead.
This code is forming a portion of a I/O library I'm building up. I suspect
I'll back off on the resolution but I haven't decided yet.
#define CLOCK_SPEED (10000000) /* Nominal speed for internal */
/* clock (timer not CPU) in
Hz. */
/* Used for converting between counts and nS for time function.*/
/* An unsigned long long so overflows are not possible during */
/* the conversion. */
#define NS_PER_COUNT (100uLL)
#define COUNTER_RESET ((unsigned char)2)
#define COUNTER_ENABLE ((unsigned char)1)
static unsigned long timing_scale_factor;
static int start_clock(
struct _reent *r)
{
unsigned long rate, divider;
rate = VPB_rate( r); /* Get the clock rate of the */
/* peripheral bus. */
if( rate == 0) {
return -1;
}
/* Set clock to reset. This will keep them at zero. */
T0TCR = COUNTER_RESET;
/* Calculate a divider that will cause the internal clock to */
/* approximate the target clock speed. This should give a */
/* clock counting rate between the desired rate and twice the */
/* desired rate. */
divider = rate/ CLOCK_SPEED;
if( divider == 0) { /* Sanity check on the divider. If we have a */
divider = 1uL; /* pclk slower than our desired rate we won't */
} /* be able to reach the desired rate so just */
/* run as fast as we can. */
T0PR = divider - 1; /* Set prescaler to give us our target rate. */
/* Since the actual clock rate we have and our target rate may */
/* not be the same, calculate a corrective factor for routines */
/* that make use of it. This value will be divided by */
/* CLOCK_SPEED when dealing with time and so should produce a */
/* result precise to 1 part in CLOCK_SPEED. */
timing_scale_factor = rate / divider;
T0MCR &= ~7u; /*lint !e915 */
T0EMR &= ~0x31u; /*lint !e915 */
T0EMR |= 0x20u; /*lint !e915 */
T0TCR = COUNTER_ENABLE; /* Enable clocks. */
return 0;
}
static unsigned long long counts_to_ns( unsigned long counts)
{
unsigned long long ns;
/* Convert the counts to nanoseconds taking the actual clock */
/* rate into account. Note: NS_PER_COUNT * CLOCK_SPEED should */
/* always be 1,000,000,000. The expanded form just shows the */
/* intent more clearly. */
ns = ( counts * NS_PER_COUNT * CLOCK_SPEED)/timing_scale_factor;
return ns;
}
static unsigned long ns_to_counts( unsigned long ns)
{
unsigned long long counts;
/* Convert the nanoseconds to counts taking the actual clock */
/* rate into account. Note: NS_PER_COUNT * CLOCK_SPEED should */
/* always be 1,000,000,000. The expanded form just shows the */
/* intent more clearly. Note 2: This can only result in a */
/* value larger than an unsigned long if ther are multiple */
/* counts in a nS, i.e. a rate > ~4GHz */
counts = ((unsigned long long)ns * timing_scale_factor)/(CLOCK_SPEED *
NS_PER_COUNT);
return (unsigned long)counts;
}
static unsigned long high_counts = 0uL;
static void accumulate_time( void)
{
static unsigned long last_time = 0uL;
unsigned long current_time;
/* A simple polled rollover check. This should work as long as*/
/* this routine is called at least once every time the counter */
/* has incremented 1/2 of its maximum count. */
current_time = T0TC; /* Get the current count */
if(current_time < last_time) { /* If we have rolled over, */
high_counts++; /* increment the high oder */
} /* accumulator. */
last_time = current_time; /* Record last read value for */
/* the next check. */
}
static unsigned long long get_full_counts( void)
{
unsigned long low;
unsigned long long full_count;
accumulate_time(); /* Check for rollover of HW clock. */
/* Read in the clock counts. It is necessary to perform a */
/* rollover check so we don't end up with the low order value */
/* that of after the rollover and the high order count from */
/* before the rollover. If that were to happen our time would */
/* not increase in a monotonic fashion. */
do {
low = T0TC; /* Read current count. */
/* Form low order and high order counts into a single value. */
full_count = ((unsigned long long)high_counts << 32) | low;
} while( low > T0TC); /* Check for rollover and redo if one */
/* has happened. */
return full_count; /* All done. */
}
static void wait_ns(
const unsigned long *ptr)
{
unsigned long counts;
accumulate_time(); /* Check for rollover of HW clock. */
counts = ns_to_counts( *ptr); /* Convert from requested wait in nS */
/* to counts used by the HW counter. */
/* If counts is at zero then the wait interval is too small */
/* and we should just return. The smallest count we can deal */
/* with is the count that occurs between reading the HW clock */
/* and setting the match register. It is quite possible there */
/* is a small non-zero count that will be missed because it is */
/* smaller than the time taken to perform those operations. The */
/* value of this minimum will depend on the timer rate and the */
/* CPU execution speed. In addition any wait will have a fixed */
/* overhead for setup and tear down. It may be possible to */
/* compensate for one or both of these on startup in a future */
/* revision. */
if( counts > 0) {
IOSET = 0x100u;
T0EMR &= ~0x31;/*lint !e915*//* Set external match 0 output to 0 */
/* and operation on match to do nothing.*/
T0MR0 = T0TC + counts; /* Set match to current time + delay */
/* time. */
T0EMR |= 0x20;/*lint !e915*//* Set operation on match to set to 1. */
while( (T0EMR & 0x1) == 0) { /* Wait for match to set output*/
} /* to 1. */
T0EMR &= ~0x31;/*lint !e915*//* Set external match 0 output to 0 */
/* and operation on match to do nothing */
/* so we don't trigger next wait early */
/* by accident. */
IOCLR = 0x100u;
}
}
Robert
" 'Freedom' has no meaning of itself. There are always restrictions,
be they legal, genetic, or physical. If you don't believe me, try to
chew a radio signal. "
Kelvin Throop, IIIMessage
RE: [lpc2100] Delay routines
2003-12-07 by Robert Adsett
Attachments
- No local attachments were found for this message.